Merge pull request #46241 from kssenii/named-collections-granular-access

Allow separate grants for every named collection
This commit is contained in:
Kseniia Sumarokova 2023-03-21 12:27:53 +01:00 committed by GitHub
commit a9bcd022d5
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
31 changed files with 622 additions and 114 deletions

View File

@ -61,14 +61,25 @@ namespace
res.any_database = true;
res.any_table = true;
res.any_column = true;
res.any_parameter = true;
break;
}
case 1:
{
res.any_database = false;
res.database = full_name[0];
res.any_table = true;
res.any_column = true;
if (access_flags.isGlobalWithParameter())
{
res.parameter = full_name[0];
res.any_parameter = false;
res.any_database = false;
}
else
{
res.database = full_name[0];
res.any_database = false;
res.any_parameter = false;
res.any_table = true;
res.any_column = true;
}
break;
}
case 2:
@ -110,10 +121,35 @@ namespace
size_t count_elements_with_diff_columns = sorted.countElementsWithDifferenceInColumnOnly(i);
if (count_elements_with_diff_columns == 1)
{
/// Easy case: one Element is converted to one AccessRightsElement.
const auto & element = sorted[i];
if (element.access_flags)
res.emplace_back(element.getResult());
{
const bool all_granted = sorted.size() == 1 && element.access_flags.contains(AccessFlags::allFlags());
if (all_granted)
{
/// Easy case: one Element is converted to one AccessRightsElement.
res.emplace_back(element.getResult());
}
else
{
auto per_parameter = element.access_flags.splitIntoParameterTypes();
if (per_parameter.size() == 1)
{
/// Easy case: one Element is converted to one AccessRightsElement.
res.emplace_back(element.getResult());
}
else
{
/// Difficult case: one element is converted into multiple AccessRightsElements.
for (const auto & [_, parameter_flags] : per_parameter)
{
auto current_element{element};
current_element.access_flags = parameter_flags;
res.emplace_back(current_element.getResult());
}
}
}
}
++i;
}
else
@ -137,6 +173,8 @@ namespace
{
return (element.full_name.size() != 3) || (element.full_name[0] != start_element.full_name[0])
|| (element.full_name[1] != start_element.full_name[1]) || (element.grant_option != start_element.grant_option)
|| (element.access_flags.isGlobalWithParameter() != start_element.access_flags.isGlobalWithParameter())
|| (element.access_flags.getParameterType() != start_element.access_flags.getParameterType())
|| (element.is_partial_revoke != start_element.is_partial_revoke);
});
@ -191,11 +229,19 @@ namespace
}
};
/**
* Levels:
* 1. GLOBAL
* 2. DATABASE_LEVEL 2. GLOBAL_WITH_PARAMETER (parameter example: named collection)
* 3. TABLE_LEVEL
* 4. COLUMN_LEVEL
*/
enum Level
{
GLOBAL_LEVEL,
DATABASE_LEVEL,
GLOBAL_WITH_PARAMETER = DATABASE_LEVEL,
TABLE_LEVEL,
COLUMN_LEVEL,
};
@ -205,7 +251,7 @@ namespace
switch (level)
{
case GLOBAL_LEVEL: return AccessFlags::allFlagsGrantableOnGlobalLevel();
case DATABASE_LEVEL: return AccessFlags::allFlagsGrantableOnDatabaseLevel();
case DATABASE_LEVEL: return AccessFlags::allFlagsGrantableOnDatabaseLevel() | AccessFlags::allFlagsGrantableOnGlobalWithParameterLevel();
case TABLE_LEVEL: return AccessFlags::allFlagsGrantableOnTableLevel();
case COLUMN_LEVEL: return AccessFlags::allFlagsGrantableOnColumnLevel();
}
@ -783,7 +829,14 @@ void AccessRights::grantImplHelper(const AccessRightsElement & element)
{
assert(!element.is_partial_revoke);
assert(!element.grant_option || with_grant_option);
if (element.any_database)
if (element.isGlobalWithParameter())
{
if (element.any_parameter)
grantImpl<with_grant_option>(element.access_flags);
else
grantImpl<with_grant_option>(element.access_flags, element.parameter);
}
else if (element.any_database)
grantImpl<with_grant_option>(element.access_flags);
else if (element.any_table)
grantImpl<with_grant_option>(element.access_flags, element.database);
@ -858,7 +911,14 @@ template <bool grant_option>
void AccessRights::revokeImplHelper(const AccessRightsElement & element)
{
assert(!element.grant_option || grant_option);
if (element.any_database)
if (element.isGlobalWithParameter())
{
if (element.any_parameter)
revokeImpl<grant_option>(element.access_flags);
else
revokeImpl<grant_option>(element.access_flags, element.parameter);
}
else if (element.any_database)
revokeImpl<grant_option>(element.access_flags);
else if (element.any_table)
revokeImpl<grant_option>(element.access_flags, element.database);
@ -948,7 +1008,14 @@ template <bool grant_option>
bool AccessRights::isGrantedImplHelper(const AccessRightsElement & element) const
{
assert(!element.grant_option || grant_option);
if (element.any_database)
if (element.isGlobalWithParameter())
{
if (element.any_parameter)
return isGrantedImpl<grant_option>(element.access_flags);
else
return isGrantedImpl<grant_option>(element.access_flags, element.parameter);
}
else if (element.any_database)
return isGrantedImpl<grant_option>(element.access_flags);
else if (element.any_table)
return isGrantedImpl<grant_option>(element.access_flags, element.database);

View File

@ -15,6 +15,7 @@ namespace ErrorCodes
{
extern const int UNKNOWN_ACCESS_TYPE;
extern const int LOGICAL_ERROR;
extern const int MIXED_ACCESS_PARAMETER_TYPES;
}
namespace
@ -96,11 +97,14 @@ namespace
const Flags & getAllFlags() const { return all_flags; }
const Flags & getGlobalFlags() const { return all_flags_for_target[GLOBAL]; }
const Flags & getGlobalWithParameterFlags() const { return all_flags_grantable_on_global_with_parameter_level; }
const Flags & getDatabaseFlags() const { return all_flags_for_target[DATABASE]; }
const Flags & getTableFlags() const { return all_flags_for_target[TABLE]; }
const Flags & getColumnFlags() const { return all_flags_for_target[COLUMN]; }
const Flags & getDictionaryFlags() const { return all_flags_for_target[DICTIONARY]; }
const Flags & getNamedCollectionFlags() const { return all_flags_for_target[NAMED_COLLECTION]; }
const Flags & getAllFlagsGrantableOnGlobalLevel() const { return getAllFlags(); }
const Flags & getAllFlagsGrantableOnGlobalWithParameterLevel() const { return getGlobalWithParameterFlags(); }
const Flags & getAllFlagsGrantableOnDatabaseLevel() const { return all_flags_grantable_on_database_level; }
const Flags & getAllFlagsGrantableOnTableLevel() const { return all_flags_grantable_on_table_level; }
const Flags & getAllFlagsGrantableOnColumnLevel() const { return getColumnFlags(); }
@ -116,6 +120,7 @@ namespace
VIEW = TABLE,
COLUMN,
DICTIONARY,
NAMED_COLLECTION,
};
struct Node;
@ -295,6 +300,7 @@ namespace
collectAllFlags(child.get());
all_flags_grantable_on_table_level = all_flags_for_target[TABLE] | all_flags_for_target[DICTIONARY] | all_flags_for_target[COLUMN];
all_flags_grantable_on_global_with_parameter_level = all_flags_for_target[NAMED_COLLECTION];
all_flags_grantable_on_database_level = all_flags_for_target[DATABASE] | all_flags_grantable_on_table_level;
}
@ -345,12 +351,44 @@ namespace
std::unordered_map<std::string_view, Flags> keyword_to_flags_map;
std::vector<Flags> access_type_to_flags_mapping;
Flags all_flags;
Flags all_flags_for_target[static_cast<size_t>(DICTIONARY) + 1];
Flags all_flags_for_target[static_cast<size_t>(NAMED_COLLECTION) + 1];
Flags all_flags_grantable_on_database_level;
Flags all_flags_grantable_on_table_level;
Flags all_flags_grantable_on_global_with_parameter_level;
};
}
bool AccessFlags::isGlobalWithParameter() const
{
return getParameterType() != AccessFlags::NONE;
}
std::unordered_map<AccessFlags::ParameterType, AccessFlags> AccessFlags::splitIntoParameterTypes() const
{
std::unordered_map<ParameterType, AccessFlags> result;
auto named_collection_flags = AccessFlags::allNamedCollectionFlags() & *this;
if (named_collection_flags)
result.emplace(ParameterType::NAMED_COLLECTION, named_collection_flags);
auto other_flags = (~AccessFlags::allNamedCollectionFlags()) & *this;
if (other_flags)
result.emplace(ParameterType::NONE, other_flags);
return result;
}
AccessFlags::ParameterType AccessFlags::getParameterType() const
{
if (isEmpty() || !AccessFlags::allGlobalWithParameterFlags().contains(*this))
return AccessFlags::NONE;
/// All flags refer to NAMED COLLECTION access type.
if (AccessFlags::allNamedCollectionFlags().contains(*this))
return AccessFlags::NAMED_COLLECTION;
throw Exception(ErrorCodes::MIXED_ACCESS_PARAMETER_TYPES, "Having mixed parameter types: {}", toString());
}
AccessFlags::AccessFlags(AccessType type) : flags(Helper::instance().accessTypeToFlags(type)) {}
AccessFlags::AccessFlags(std::string_view keyword) : flags(Helper::instance().keywordToFlags(keyword)) {}
@ -361,11 +399,14 @@ std::vector<AccessType> AccessFlags::toAccessTypes() const { return Helper::inst
std::vector<std::string_view> AccessFlags::toKeywords() const { return Helper::instance().flagsToKeywords(flags); }
AccessFlags AccessFlags::allFlags() { return Helper::instance().getAllFlags(); }
AccessFlags AccessFlags::allGlobalFlags() { return Helper::instance().getGlobalFlags(); }
AccessFlags AccessFlags::allGlobalWithParameterFlags() { return Helper::instance().getGlobalWithParameterFlags(); }
AccessFlags AccessFlags::allDatabaseFlags() { return Helper::instance().getDatabaseFlags(); }
AccessFlags AccessFlags::allTableFlags() { return Helper::instance().getTableFlags(); }
AccessFlags AccessFlags::allColumnFlags() { return Helper::instance().getColumnFlags(); }
AccessFlags AccessFlags::allDictionaryFlags() { return Helper::instance().getDictionaryFlags(); }
AccessFlags AccessFlags::allNamedCollectionFlags() { return Helper::instance().getNamedCollectionFlags(); }
AccessFlags AccessFlags::allFlagsGrantableOnGlobalLevel() { return Helper::instance().getAllFlagsGrantableOnGlobalLevel(); }
AccessFlags AccessFlags::allFlagsGrantableOnGlobalWithParameterLevel() { return Helper::instance().getAllFlagsGrantableOnGlobalWithParameterLevel(); }
AccessFlags AccessFlags::allFlagsGrantableOnDatabaseLevel() { return Helper::instance().getAllFlagsGrantableOnDatabaseLevel(); }
AccessFlags AccessFlags::allFlagsGrantableOnTableLevel() { return Helper::instance().getAllFlagsGrantableOnTableLevel(); }
AccessFlags AccessFlags::allFlagsGrantableOnColumnLevel() { return Helper::instance().getAllFlagsGrantableOnColumnLevel(); }

View File

@ -48,8 +48,17 @@ public:
AccessFlags operator ~() const { AccessFlags res; res.flags = ~flags; return res; }
bool isEmpty() const { return flags.none(); }
bool isAll() const { return flags.all(); }
explicit operator bool() const { return !isEmpty(); }
bool contains(const AccessFlags & other) const { return (flags & other.flags) == other.flags; }
bool isGlobalWithParameter() const;
enum ParameterType
{
NONE,
NAMED_COLLECTION,
};
ParameterType getParameterType() const;
std::unordered_map<ParameterType, AccessFlags> splitIntoParameterTypes() const;
friend bool operator ==(const AccessFlags & left, const AccessFlags & right) { return left.flags == right.flags; }
friend bool operator !=(const AccessFlags & left, const AccessFlags & right) { return !(left == right); }
@ -76,6 +85,8 @@ public:
/// Returns all the global flags.
static AccessFlags allGlobalFlags();
static AccessFlags allGlobalWithParameterFlags();
/// Returns all the flags related to a database.
static AccessFlags allDatabaseFlags();
@ -88,10 +99,16 @@ public:
/// Returns all the flags related to a dictionary.
static AccessFlags allDictionaryFlags();
/// Returns all the flags related to a named collection.
static AccessFlags allNamedCollectionFlags();
/// Returns all the flags which could be granted on the global level.
/// The same as allFlags().
static AccessFlags allFlagsGrantableOnGlobalLevel();
/// Returns all the flags which could be granted on the global with parameter level.
static AccessFlags allFlagsGrantableOnGlobalWithParameterLevel();
/// Returns all the flags which could be granted on the database level.
/// Returns allDatabaseFlags() | allTableFlags() | allDictionaryFlags() | allColumnFlags().
static AccessFlags allFlagsGrantableOnDatabaseLevel();

View File

@ -21,24 +21,31 @@ namespace
result += ")";
}
void formatONClause(const String & database, bool any_database, const String & table, bool any_table, String & result)
void formatONClause(const AccessRightsElement & element, String & result)
{
result += "ON ";
if (any_database)
if (element.isGlobalWithParameter())
{
if (element.any_parameter)
result += "*";
else
result += backQuoteIfNeed(element.parameter);
}
else if (element.any_database)
{
result += "*.*";
}
else
{
if (!database.empty())
if (!element.database.empty())
{
result += backQuoteIfNeed(database);
result += backQuoteIfNeed(element.database);
result += ".";
}
if (any_table)
if (element.any_table)
result += "*";
else
result += backQuoteIfNeed(table);
result += backQuoteIfNeed(element.table);
}
}
@ -96,7 +103,7 @@ namespace
String result;
formatAccessFlagsWithColumns(element.access_flags, element.columns, element.any_column, result);
result += " ";
formatONClause(element.database, element.any_database, element.table, element.any_table, result);
formatONClause(element, result);
if (with_options)
formatOptions(element.grant_option, element.is_partial_revoke, result);
return result;
@ -122,14 +129,16 @@ namespace
if (i != elements.size() - 1)
{
const auto & next_element = elements[i + 1];
if (element.sameDatabaseAndTable(next_element) && element.sameOptions(next_element))
if (element.sameDatabaseAndTableAndParameter(next_element) && element.sameOptions(next_element))
{
next_element_uses_same_table_and_options = true;
}
}
if (!next_element_uses_same_table_and_options)
{
part += " ";
formatONClause(element.database, element.any_database, element.table, element.any_table, part);
formatONClause(element, part);
if (with_options)
formatOptions(element.grant_option, element.is_partial_revoke, part);
if (result.empty())
@ -164,6 +173,7 @@ AccessRightsElement::AccessRightsElement(
, any_database(false)
, any_table(false)
, any_column(false)
, any_parameter(false)
{
}
@ -188,12 +198,15 @@ AccessRightsElement::AccessRightsElement(
, any_database(false)
, any_table(false)
, any_column(false)
, any_parameter(false)
{
}
void AccessRightsElement::eraseNonGrantable()
{
if (!any_column)
if (isGlobalWithParameter() && !any_parameter)
access_flags &= AccessFlags::allFlagsGrantableOnGlobalWithParameterLevel();
else if (!any_column)
access_flags &= AccessFlags::allFlagsGrantableOnColumnLevel();
else if (!any_table)
access_flags &= AccessFlags::allFlagsGrantableOnTableLevel();
@ -215,6 +228,11 @@ String AccessRightsElement::toStringWithoutOptions() const { return toStringImpl
bool AccessRightsElements::empty() const { return std::all_of(begin(), end(), [](const AccessRightsElement & e) { return e.empty(); }); }
bool AccessRightsElements::sameDatabaseAndTableAndParameter() const
{
return (size() < 2) || std::all_of(std::next(begin()), end(), [this](const AccessRightsElement & e) { return e.sameDatabaseAndTableAndParameter(front()); });
}
bool AccessRightsElements::sameDatabaseAndTable() const
{
return (size() < 2) || std::all_of(std::next(begin()), end(), [this](const AccessRightsElement & e) { return e.sameDatabaseAndTable(front()); });

View File

@ -11,12 +11,17 @@ namespace DB
struct AccessRightsElement
{
AccessFlags access_flags;
String database;
String table;
Strings columns;
String parameter;
bool any_database = true;
bool any_table = true;
bool any_column = true;
bool any_parameter = false;
bool grant_option = false;
bool is_partial_revoke = false;
@ -44,14 +49,26 @@ struct AccessRightsElement
bool empty() const { return !access_flags || (!any_column && columns.empty()); }
auto toTuple() const { return std::tie(access_flags, any_database, database, any_table, table, any_column, columns, grant_option, is_partial_revoke); }
auto toTuple() const { return std::tie(access_flags, any_database, database, any_table, table, any_column, columns, any_parameter, parameter, grant_option, is_partial_revoke); }
friend bool operator==(const AccessRightsElement & left, const AccessRightsElement & right) { return left.toTuple() == right.toTuple(); }
friend bool operator!=(const AccessRightsElement & left, const AccessRightsElement & right) { return !(left == right); }
bool sameDatabaseAndTableAndParameter(const AccessRightsElement & other) const
{
return sameDatabaseAndTable(other) && sameParameter(other);
}
bool sameParameter(const AccessRightsElement & other) const
{
return (parameter == other.parameter) && (any_parameter == other.any_parameter)
&& (access_flags.getParameterType() == other.access_flags.getParameterType())
&& (isGlobalWithParameter() == other.isGlobalWithParameter());
}
bool sameDatabaseAndTable(const AccessRightsElement & other) const
{
return (database == other.database) && (any_database == other.any_database) && (table == other.table)
&& (any_table == other.any_table);
return (database == other.database) && (any_database == other.any_database)
&& (table == other.table) && (any_table == other.any_table);
}
bool sameOptions(const AccessRightsElement & other) const
@ -67,6 +84,8 @@ struct AccessRightsElement
/// If the database is empty, replaces it with `current_database`. Otherwise does nothing.
void replaceEmptyDatabase(const String & current_database);
bool isGlobalWithParameter() const { return access_flags.isGlobalWithParameter(); }
/// Returns a human-readable representation like "GRANT SELECT, UPDATE(x, y) ON db.table".
String toString() const;
String toStringWithoutOptions() const;
@ -81,6 +100,7 @@ public:
using Base::Base;
bool empty() const;
bool sameDatabaseAndTableAndParameter() const;
bool sameDatabaseAndTable() const;
bool sameOptions() const;

View File

@ -12,7 +12,7 @@ enum class AccessType
/// Macro M should be defined as M(name, aliases, node_type, parent_group_name)
/// where name is identifier with underscores (instead of spaces);
/// aliases is a string containing comma-separated list;
/// node_type either specifies access type's level (GLOBAL/DATABASE/TABLE/DICTIONARY/VIEW/COLUMNS),
/// node_type either specifies access type's level (GLOBAL/NAMED_COLLECTION/DATABASE/TABLE/DICTIONARY/VIEW/COLUMNS),
/// or specifies that the access type is a GROUP of other access types;
/// parent_group_name is the name of the group containing this access type (or NONE if there is no such group).
/// NOTE A parent group must be declared AFTER all its children.
@ -70,7 +70,7 @@ enum class AccessType
M(ALTER_FREEZE_PARTITION, "FREEZE PARTITION, UNFREEZE", TABLE, ALTER_TABLE) \
\
M(ALTER_DATABASE_SETTINGS, "ALTER DATABASE SETTING, ALTER MODIFY DATABASE SETTING, MODIFY DATABASE SETTING", DATABASE, ALTER_DATABASE) /* allows to execute ALTER MODIFY SETTING */\
M(ALTER_NAMED_COLLECTION, "", GROUP, ALTER) /* allows to execute ALTER NAMED COLLECTION */\
M(ALTER_NAMED_COLLECTION, "", NAMED_COLLECTION, NAMED_COLLECTION_CONTROL) /* allows to execute ALTER NAMED COLLECTION */\
\
M(ALTER_TABLE, "", GROUP, ALTER) \
M(ALTER_DATABASE, "", GROUP, ALTER) \
@ -92,7 +92,7 @@ enum class AccessType
M(CREATE_ARBITRARY_TEMPORARY_TABLE, "", GLOBAL, CREATE) /* allows to create and manipulate temporary tables
with arbitrary table engine */\
M(CREATE_FUNCTION, "", GLOBAL, CREATE) /* allows to execute CREATE FUNCTION */ \
M(CREATE_NAMED_COLLECTION, "", GLOBAL, CREATE) /* allows to execute CREATE NAMED COLLECTION */ \
M(CREATE_NAMED_COLLECTION, "", NAMED_COLLECTION, NAMED_COLLECTION_CONTROL) /* allows to execute CREATE NAMED COLLECTION */ \
M(CREATE, "", GROUP, ALL) /* allows to execute {CREATE|ATTACH} */ \
\
M(DROP_DATABASE, "", DATABASE, DROP) /* allows to execute {DROP|DETACH} DATABASE */\
@ -101,7 +101,7 @@ enum class AccessType
implicitly enabled by the grant DROP_TABLE */\
M(DROP_DICTIONARY, "", DICTIONARY, DROP) /* allows to execute {DROP|DETACH} DICTIONARY */\
M(DROP_FUNCTION, "", GLOBAL, DROP) /* allows to execute DROP FUNCTION */\
M(DROP_NAMED_COLLECTION, "", GLOBAL, DROP) /* allows to execute DROP NAMED COLLECTION */\
M(DROP_NAMED_COLLECTION, "", NAMED_COLLECTION, NAMED_COLLECTION_CONTROL) /* allows to execute DROP NAMED COLLECTION */\
M(DROP, "", GROUP, ALL) /* allows to execute {DROP|DETACH} */\
\
M(TRUNCATE, "TRUNCATE TABLE", TABLE, ALL) \
@ -137,9 +137,10 @@ enum class AccessType
M(SHOW_QUOTAS, "SHOW CREATE QUOTA", GLOBAL, SHOW_ACCESS) \
M(SHOW_SETTINGS_PROFILES, "SHOW PROFILES, SHOW CREATE SETTINGS PROFILE, SHOW CREATE PROFILE", GLOBAL, SHOW_ACCESS) \
M(SHOW_ACCESS, "", GROUP, ACCESS_MANAGEMENT) \
M(SHOW_NAMED_COLLECTIONS, "SHOW NAMED COLLECTIONS", GLOBAL, ACCESS_MANAGEMENT) \
M(SHOW_NAMED_COLLECTIONS_SECRETS, "SHOW NAMED COLLECTIONS SECRETS", GLOBAL, ACCESS_MANAGEMENT) \
M(ACCESS_MANAGEMENT, "", GROUP, ALL) \
M(SHOW_NAMED_COLLECTIONS, "SHOW NAMED COLLECTIONS", NAMED_COLLECTION, NAMED_COLLECTION_CONTROL) \
M(SHOW_NAMED_COLLECTIONS_SECRETS, "SHOW NAMED COLLECTIONS SECRETS", NAMED_COLLECTION, NAMED_COLLECTION_CONTROL) \
M(NAMED_COLLECTION_CONTROL, "", NAMED_COLLECTION, ALL) \
\
M(SYSTEM_SHUTDOWN, "SYSTEM KILL, SHUTDOWN", GLOBAL, SYSTEM) \
M(SYSTEM_DROP_DNS_CACHE, "SYSTEM DROP DNS, DROP DNS CACHE, DROP DNS", GLOBAL, SYSTEM_DROP_CACHE) \

View File

@ -507,13 +507,17 @@ bool ContextAccess::checkAccessImplHelper(AccessFlags flags, const Args &... arg
if (!flags)
return true;
/// Access to temporary tables is controlled in an unusual way, not like normal tables.
/// Creating of temporary tables is controlled by AccessType::CREATE_TEMPORARY_TABLES grant,
/// and other grants are considered as always given.
/// The DatabaseCatalog class won't resolve StorageID for temporary tables
/// which shouldn't be accessed.
if (getDatabase(args...) == DatabaseCatalog::TEMPORARY_DATABASE)
return access_granted();
const auto parameter_type = flags.getParameterType();
if (parameter_type == AccessFlags::NONE)
{
/// Access to temporary tables is controlled in an unusual way, not like normal tables.
/// Creating of temporary tables is controlled by AccessType::CREATE_TEMPORARY_TABLES grant,
/// and other grants are considered as always given.
/// The DatabaseCatalog class won't resolve StorageID for temporary tables
/// which shouldn't be accessed.
if (getDatabase(args...) == DatabaseCatalog::TEMPORARY_DATABASE)
return access_granted();
}
auto acs = getAccessRightsWithImplicit();
bool granted;
@ -611,7 +615,14 @@ template <bool throw_if_denied, bool grant_option>
bool ContextAccess::checkAccessImplHelper(const AccessRightsElement & element) const
{
assert(!element.grant_option || grant_option);
if (element.any_database)
if (element.isGlobalWithParameter())
{
if (element.any_parameter)
return checkAccessImpl<throw_if_denied, grant_option>(element.access_flags);
else
return checkAccessImpl<throw_if_denied, grant_option>(element.access_flags, element.parameter);
}
else if (element.any_database)
return checkAccessImpl<throw_if_denied, grant_option>(element.access_flags);
else if (element.any_table)
return checkAccessImpl<throw_if_denied, grant_option>(element.access_flags, element.database);

View File

@ -233,10 +233,10 @@ namespace
user->access.revokeGrantOption(AccessType::ALL);
}
bool show_named_collections = config.getBool(user_config + ".show_named_collections", false);
if (!show_named_collections)
bool named_collection_control = config.getBool(user_config + ".named_collection_control", false);
if (!named_collection_control)
{
user->access.revoke(AccessType::SHOW_NAMED_COLLECTIONS);
user->access.revoke(AccessType::NAMED_COLLECTION_CONTROL);
}
bool show_named_collections_secrets = config.getBool(user_config + ".show_named_collections_secrets", false);

View File

@ -53,7 +53,7 @@ TEST(AccessRights, Union)
"SHOW ROW POLICIES, SYSTEM MERGES, SYSTEM TTL MERGES, SYSTEM FETCHES, "
"SYSTEM MOVES, SYSTEM SENDS, SYSTEM REPLICATION QUEUES, "
"SYSTEM DROP REPLICA, SYSTEM SYNC REPLICA, SYSTEM RESTART REPLICA, "
"SYSTEM RESTORE REPLICA, SYSTEM WAIT LOADING PARTS, SYSTEM SYNC DATABASE REPLICA, SYSTEM FLUSH DISTRIBUTED, dictGet ON db1.*");
"SYSTEM RESTORE REPLICA, SYSTEM WAIT LOADING PARTS, SYSTEM SYNC DATABASE REPLICA, SYSTEM FLUSH DISTRIBUTED, dictGet ON db1.*, GRANT NAMED COLLECTION CONTROL ON db1");
}

View File

@ -648,6 +648,7 @@
M(677, THREAD_WAS_CANCELED) \
M(678, IO_URING_INIT_FAILED) \
M(679, IO_URING_SUBMIT_ERROR) \
M(690, MIXED_ACCESS_PARAMETER_TYPES) \
\
M(999, KEEPER_EXCEPTION) \
M(1000, POCO_EXCEPTION) \

View File

@ -48,7 +48,7 @@ namespace
if (current_query)
{
const auto & prev_element = current_query->access_rights_elements.back();
bool continue_with_current_query = element.sameDatabaseAndTable(prev_element) && element.sameOptions(prev_element);
bool continue_with_current_query = element.sameDatabaseAndTableAndParameter(prev_element) && element.sameOptions(prev_element);
if (!continue_with_current_query)
current_query = nullptr;
}

View File

@ -12,9 +12,10 @@ namespace DB
BlockIO InterpreterAlterNamedCollectionQuery::execute()
{
auto current_context = getContext();
current_context->checkAccess(AccessType::ALTER_NAMED_COLLECTION);
const auto & query = query_ptr->as<const ASTAlterNamedCollectionQuery &>();
current_context->checkAccess(AccessType::ALTER_NAMED_COLLECTION, query.collection_name);
if (!query.cluster.empty())
{
DDLQueryOnClusterParams params;

View File

@ -13,10 +13,10 @@ namespace DB
BlockIO InterpreterCreateNamedCollectionQuery::execute()
{
auto current_context = getContext();
current_context->checkAccess(AccessType::CREATE_NAMED_COLLECTION);
const auto & query = query_ptr->as<const ASTCreateNamedCollectionQuery &>();
current_context->checkAccess(AccessType::CREATE_NAMED_COLLECTION, query.collection_name);
if (!query.cluster.empty())
{
DDLQueryOnClusterParams params;

View File

@ -12,9 +12,10 @@ namespace DB
BlockIO InterpreterDropNamedCollectionQuery::execute()
{
auto current_context = getContext();
current_context->checkAccess(AccessType::DROP_NAMED_COLLECTION);
const auto & query = query_ptr->as<const ASTDropNamedCollectionQuery &>();
current_context->checkAccess(AccessType::DROP_NAMED_COLLECTION, query.collection_name);
if (!query.cluster.empty())
{
DDLQueryOnClusterParams params;

View File

@ -27,21 +27,28 @@ namespace
}
void formatONClause(const String & database, bool any_database, const String & table, bool any_table, const IAST::FormatSettings & settings)
void formatONClause(const AccessRightsElement & element, const IAST::FormatSettings & settings)
{
settings.ostr << (settings.hilite ? IAST::hilite_keyword : "") << "ON " << (settings.hilite ? IAST::hilite_none : "");
if (any_database)
if (element.isGlobalWithParameter())
{
if (element.any_parameter)
settings.ostr << "*";
else
settings.ostr << backQuoteIfNeed(element.parameter);
}
else if (element.any_database)
{
settings.ostr << "*.*";
}
else
{
if (!database.empty())
settings.ostr << backQuoteIfNeed(database) << ".";
if (any_table)
if (!element.database.empty())
settings.ostr << backQuoteIfNeed(element.database) << ".";
if (element.any_table)
settings.ostr << "*";
else
settings.ostr << backQuoteIfNeed(table);
settings.ostr << backQuoteIfNeed(element.table);
}
}
@ -70,15 +77,16 @@ namespace
if (i != elements.size() - 1)
{
const auto & next_element = elements[i + 1];
if ((element.database == next_element.database) && (element.any_database == next_element.any_database)
&& (element.table == next_element.table) && (element.any_table == next_element.any_table))
if (element.sameDatabaseAndTableAndParameter(next_element))
{
next_element_on_same_db_and_table = true;
}
}
if (!next_element_on_same_db_and_table)
{
settings.ostr << " ";
formatONClause(element.database, element.any_database, element.table, element.any_table, settings);
formatONClause(element, settings);
}
}

View File

@ -123,13 +123,40 @@ namespace
if (!parseAccessFlagsWithColumns(pos, expected, access_and_columns))
return false;
String database_name, table_name, parameter;
bool any_database = false, any_table = false, any_parameter = false;
size_t is_global_with_parameter = 0;
for (const auto & elem : access_and_columns)
{
if (elem.first.isGlobalWithParameter())
++is_global_with_parameter;
}
if (!ParserKeyword{"ON"}.ignore(pos, expected))
return false;
String database_name, table_name;
bool any_database = false, any_table = false;
if (!parseDatabaseAndTableNameOrAsterisks(pos, expected, database_name, any_database, table_name, any_table))
if (is_global_with_parameter && is_global_with_parameter == access_and_columns.size())
{
ASTPtr parameter_ast;
if (ParserToken{TokenType::Asterisk}.ignore(pos, expected))
{
any_parameter = true;
}
else if (ParserIdentifier{}.parse(pos, parameter_ast, expected))
{
any_parameter = false;
parameter = getIdentifierName(parameter_ast);
}
else
return false;
any_database = any_table = true;
}
else if (!parseDatabaseAndTableNameOrAsterisks(pos, expected, database_name, any_database, table_name, any_table))
{
return false;
}
for (auto & [access_flags, columns] : access_and_columns)
{
@ -140,7 +167,9 @@ namespace
element.any_database = any_database;
element.database = database_name;
element.any_table = any_table;
element.any_parameter = any_parameter;
element.table = table_name;
element.parameter = parameter;
res_elements.emplace_back(std::move(element));
}
@ -173,6 +202,8 @@ namespace
throw Exception(ErrorCodes::INVALID_GRANT, "{} cannot be granted on the table level", old_flags.toString());
else if (!element.any_database)
throw Exception(ErrorCodes::INVALID_GRANT, "{} cannot be granted on the database level", old_flags.toString());
else if (!element.any_parameter)
throw Exception(ErrorCodes::INVALID_GRANT, "{} cannot be granted on the global with parameter level", old_flags.toString());
else
throw Exception(ErrorCodes::INVALID_GRANT, "{} cannot be granted", old_flags.toString());
});

View File

@ -7,9 +7,9 @@
#include <Interpreters/ProfileEventsExt.h>
#include <Access/Common/AccessType.h>
#include <Access/Common/AccessFlags.h>
#include <Access/ContextAccess.h>
#include <Columns/ColumnMap.h>
#include <Common/NamedCollections/NamedCollections.h>
#include <Access/ContextAccess.h>
namespace DB
@ -30,7 +30,6 @@ StorageSystemNamedCollections::StorageSystemNamedCollections(const StorageID & t
void StorageSystemNamedCollections::fillData(MutableColumns & res_columns, ContextPtr context, const SelectQueryInfo &) const
{
context->checkAccess(AccessType::SHOW_NAMED_COLLECTIONS);
const auto & access = context->getAccess();
NamedCollectionUtils::loadIfNot();
@ -38,6 +37,9 @@ void StorageSystemNamedCollections::fillData(MutableColumns & res_columns, Conte
auto collections = NamedCollectionFactory::instance().getAll();
for (const auto & [name, collection] : collections)
{
if (!access->isGranted(AccessType::SHOW_NAMED_COLLECTIONS, name))
continue;
res_columns[0]->insert(name);
auto * column_map = typeid_cast<ColumnMap *>(res_columns[1].get());

View File

@ -28,6 +28,7 @@ namespace
DICTIONARY,
VIEW,
COLUMN,
NAMED_COLLECTION,
};
DataTypeEnum8::Values getLevelEnumValues()
@ -39,6 +40,7 @@ namespace
enum_values.emplace_back("DICTIONARY", static_cast<Int8>(DICTIONARY));
enum_values.emplace_back("VIEW", static_cast<Int8>(VIEW));
enum_values.emplace_back("COLUMN", static_cast<Int8>(COLUMN));
enum_values.emplace_back("NAMED_COLLECTION", static_cast<Int8>(NAMED_COLLECTION));
return enum_values;
}
}

View File

@ -2,7 +2,7 @@
<users>
<default>
<access_management>1</access_management>
<show_named_collections>1</show_named_collections>
<named_collection_control>1</named_collection_control>
<show_named_collections_secrets>1</show_named_collections_secrets>
</default>
</users>

View File

@ -4,7 +4,7 @@
<password></password>
<profile>default</profile>
<quota>default</quota>
<show_named_collections>1</show_named_collections>
<named_collection_control>1</named_collection_control>
<show_named_collections_secrets>1</show_named_collections_secrets>
</default>
</users>

View File

@ -4,7 +4,7 @@
<password></password>
<profile>default</profile>
<quota>default</quota>
<show_named_collections>1</show_named_collections>
<named_collection_control>1</named_collection_control>
<show_named_collections_secrets>1</show_named_collections_secrets>
</default>
</users>

View File

@ -4,7 +4,7 @@
<password></password>
<profile>default</profile>
<quota>default</quota>
<show_named_collections>1</show_named_collections>
<named_collection_control>1</named_collection_control>
<show_named_collections_secrets>1</show_named_collections_secrets>
</default>
</users>

View File

@ -4,7 +4,7 @@
<password></password>
<profile>default</profile>
<quota>default</quota>
<show_named_collections>1</show_named_collections>
<named_collection_control>1</named_collection_control>
<show_named_collections_secrets>1</show_named_collections_secrets>
</default>
</users>

View File

@ -402,6 +402,9 @@ def test_introspection():
assert instance.query("SHOW GRANTS FOR B") == TSV(
["GRANT CREATE ON *.* TO B WITH GRANT OPTION"]
)
assert instance.query("SHOW GRANTS FOR default") == TSV(
["GRANT ALL ON *.* TO default WITH GRANT OPTION"]
)
assert instance.query("SHOW GRANTS FOR A,B") == TSV(
[
"GRANT SELECT ON test.table TO A",

View File

@ -4,6 +4,7 @@
<password></password>
<profile>default</profile>
<quota>default</quota>
<named_collection_control>1</named_collection_control>
<show_named_collections>1</show_named_collections>
<show_named_collections_secrets>1</show_named_collections_secrets>
</default>

View File

@ -4,7 +4,7 @@
<password></password>
<profile>default</profile>
<quota>default</quota>
<access_management>1</access_management>
<named_collection_control>1</named_collection_control>
</default>
</users>
</clickhouse>

View File

@ -24,6 +24,16 @@ def cluster():
],
stay_alive=True,
)
cluster.add_instance(
"node_only_named_collection_control",
main_configs=[
"configs/config.d/named_collections.xml",
],
user_configs=[
"configs/users.d/users_only_named_collection_control.xml",
],
stay_alive=True,
)
cluster.add_instance(
"node_no_default_access",
main_configs=[
@ -34,16 +44,6 @@ def cluster():
],
stay_alive=True,
)
cluster.add_instance(
"node_no_default_access_but_with_access_management",
main_configs=[
"configs/config.d/named_collections.xml",
],
user_configs=[
"configs/users.d/users_no_default_access_with_access_management.xml",
],
stay_alive=True,
)
logging.info("Starting cluster...")
cluster.start()
@ -70,40 +70,39 @@ def replace_in_users_config(node, old, new):
)
def test_access(cluster):
def test_default_access(cluster):
node = cluster.instances["node_no_default_access"]
assert 0 == int(node.query("select count() from system.named_collections"))
node = cluster.instances["node_only_named_collection_control"]
assert 1 == int(node.query("select count() from system.named_collections"))
assert (
"DB::Exception: default: Not enough privileges. To execute this query it's necessary to have grant SHOW NAMED COLLECTIONS ON *.*"
in node.query_and_get_error("select count() from system.named_collections")
)
node = cluster.instances["node_no_default_access_but_with_access_management"]
assert (
"DB::Exception: default: Not enough privileges. To execute this query it's necessary to have grant SHOW NAMED COLLECTIONS ON *.*"
in node.query_and_get_error("select count() from system.named_collections")
node.query("select collection['key1'] from system.named_collections").strip()
== "[HIDDEN]"
)
node = cluster.instances["node"]
assert int(node.query("select count() from system.named_collections")) > 0
replace_in_users_config(
node, "show_named_collections>1", "show_named_collections>0"
node, "named_collection_control>1", "named_collection_control>0"
)
assert "show_named_collections>0" in node.exec_in_container(
assert "named_collection_control>0" in node.exec_in_container(
["bash", "-c", f"cat /etc/clickhouse-server/users.d/users.xml"]
)
node.restart_clickhouse()
assert 0 == int(node.query("select count() from system.named_collections"))
replace_in_users_config(
node, "named_collection_control>0", "named_collection_control>1"
)
assert "named_collection_control>1" in node.exec_in_container(
["bash", "-c", f"cat /etc/clickhouse-server/users.d/users.xml"]
)
node.restart_clickhouse()
assert (
"DB::Exception: default: Not enough privileges. To execute this query it's necessary to have grant SHOW NAMED COLLECTIONS ON *.*"
in node.query_and_get_error("select count() from system.named_collections")
)
replace_in_users_config(
node, "show_named_collections>0", "show_named_collections>1"
)
assert "show_named_collections>1" in node.exec_in_container(
["bash", "-c", f"cat /etc/clickhouse-server/users.d/users.xml"]
)
node.restart_clickhouse()
assert (
node.query("select collection['key1'] from system.named_collections").strip()
node.query(
"select collection['key1'] from system.named_collections where name = 'collection1'"
).strip()
== "value1"
)
replace_in_users_config(
@ -114,7 +113,9 @@ def test_access(cluster):
)
node.restart_clickhouse()
assert (
node.query("select collection['key1'] from system.named_collections").strip()
node.query(
"select collection['key1'] from system.named_collections where name = 'collection1'"
).strip()
== "[HIDDEN]"
)
replace_in_users_config(
@ -125,11 +126,282 @@ def test_access(cluster):
)
node.restart_clickhouse()
assert (
node.query("select collection['key1'] from system.named_collections").strip()
node.query(
"select collection['key1'] from system.named_collections where name = 'collection1'"
).strip()
== "value1"
)
def test_granular_access_show_query(cluster):
node = cluster.instances["node"]
assert (
"GRANT ALL ON *.* TO default WITH GRANT OPTION"
== node.query("SHOW GRANTS FOR default").strip()
) # includes named collections control
assert 1 == int(node.query("SELECT count() FROM system.named_collections"))
assert (
"collection1" == node.query("SELECT name FROM system.named_collections").strip()
)
node.query("DROP USER IF EXISTS kek")
node.query("CREATE USER kek")
node.query("GRANT select ON *.* TO kek")
assert 0 == int(
node.query("SELECT count() FROM system.named_collections", user="kek")
)
node.query("GRANT show named collections ON collection1 TO kek")
assert 1 == int(
node.query("SELECT count() FROM system.named_collections", user="kek")
)
assert (
"collection1"
== node.query("SELECT name FROM system.named_collections", user="kek").strip()
)
node.query("CREATE NAMED COLLECTION collection2 AS key1=1, key2='value2'")
assert 2 == int(node.query("SELECT count() FROM system.named_collections"))
assert (
"collection1\ncollection2"
== node.query("select name from system.named_collections").strip()
)
assert 1 == int(
node.query("SELECT count() FROM system.named_collections", user="kek")
)
assert (
"collection1"
== node.query("select name from system.named_collections", user="kek").strip()
)
node.query("GRANT show named collections ON collection2 TO kek")
assert 2 == int(
node.query("SELECT count() FROM system.named_collections", user="kek")
)
assert (
"collection1\ncollection2"
== node.query("select name from system.named_collections", user="kek").strip()
)
node.restart_clickhouse()
assert (
"collection1\ncollection2"
== node.query("select name from system.named_collections", user="kek").strip()
)
# check:
# GRANT show named collections ON *
# REVOKE show named collections ON collection
node.query("DROP USER IF EXISTS koko")
node.query("CREATE USER koko")
node.query("GRANT select ON *.* TO koko")
assert 0 == int(
node.query("SELECT count() FROM system.named_collections", user="koko")
)
assert "GRANT SELECT ON *.* TO koko" == node.query("SHOW GRANTS FOR koko;").strip()
node.query("GRANT show named collections ON * TO koko")
assert (
"GRANT SELECT ON *.* TO koko\nGRANT SHOW NAMED COLLECTIONS ON * TO koko"
== node.query("SHOW GRANTS FOR koko;").strip()
)
assert (
"collection1\ncollection2"
== node.query("select name from system.named_collections", user="koko").strip()
)
node.restart_clickhouse()
assert (
"GRANT SELECT ON *.* TO koko\nGRANT SHOW NAMED COLLECTIONS ON * TO koko"
== node.query("SHOW GRANTS FOR koko;").strip()
)
assert (
"collection1\ncollection2"
== node.query("select name from system.named_collections", user="koko").strip()
)
node.query("REVOKE show named collections ON collection1 FROM koko;")
assert (
"GRANT SELECT ON *.* TO koko\nGRANT SHOW NAMED COLLECTIONS ON * TO koko\nREVOKE SHOW NAMED COLLECTIONS ON collection1 FROM koko"
== node.query("SHOW GRANTS FOR koko;").strip()
)
assert (
"collection2"
== node.query("select name from system.named_collections", user="koko").strip()
)
node.restart_clickhouse()
assert (
"GRANT SELECT ON *.* TO koko\nGRANT SHOW NAMED COLLECTIONS ON * TO koko\nREVOKE SHOW NAMED COLLECTIONS ON collection1 FROM koko"
== node.query("SHOW GRANTS FOR koko;").strip()
)
assert (
"collection2"
== node.query("select name from system.named_collections", user="koko").strip()
)
node.query("REVOKE show named collections ON collection2 FROM koko;")
assert (
"" == node.query("select * from system.named_collections", user="koko").strip()
)
assert (
"GRANT SELECT ON *.* TO koko\nGRANT SHOW NAMED COLLECTIONS ON * TO koko\nREVOKE SHOW NAMED COLLECTIONS ON collection1 FROM koko\nREVOKE SHOW NAMED COLLECTIONS ON collection2 FROM koko"
== node.query("SHOW GRANTS FOR koko;").strip()
)
# check:
# GRANT show named collections ON collection
# REVOKE show named collections ON *
node.query("GRANT show named collections ON collection2 TO koko")
assert (
"GRANT SELECT ON *.* TO koko\nGRANT SHOW NAMED COLLECTIONS ON * TO koko\nREVOKE SHOW NAMED COLLECTIONS ON collection1 FROM koko"
== node.query("SHOW GRANTS FOR koko;").strip()
)
assert (
"collection2"
== node.query("select name from system.named_collections", user="koko").strip()
)
node.query("REVOKE show named collections ON * FROM koko;")
assert "GRANT SELECT ON *.* TO koko" == node.query("SHOW GRANTS FOR koko;").strip()
assert (
"" == node.query("select * from system.named_collections", user="koko").strip()
)
node.query("DROP NAMED COLLECTION collection2")
def test_show_grants(cluster):
node = cluster.instances["node"]
node.query("DROP USER IF EXISTS koko")
node.query("CREATE USER koko")
node.query("GRANT CREATE NAMED COLLECTION ON name1 TO koko")
node.query("GRANT select ON name1.* TO koko")
assert (
"GRANT SELECT ON name1.* TO koko\nGRANT CREATE NAMED COLLECTION ON name1 TO koko"
== node.query("SHOW GRANTS FOR koko;").strip()
)
node.query("DROP USER IF EXISTS koko")
node.query("CREATE USER koko")
node.query("GRANT CREATE NAMED COLLECTION ON name1 TO koko")
node.query("GRANT select ON name1 TO koko")
assert (
"GRANT SELECT ON default.name1 TO koko\nGRANT CREATE NAMED COLLECTION ON name1 TO koko"
== node.query("SHOW GRANTS FOR koko;").strip()
)
node.query("DROP USER IF EXISTS koko")
node.query("CREATE USER koko")
node.query("GRANT select ON name1 TO koko")
node.query("GRANT CREATE NAMED COLLECTION ON name1 TO koko")
assert (
"GRANT SELECT ON default.name1 TO koko\nGRANT CREATE NAMED COLLECTION ON name1 TO koko"
== node.query("SHOW GRANTS FOR koko;").strip()
)
node.query("DROP USER IF EXISTS koko")
node.query("CREATE USER koko")
node.query("GRANT select ON *.* TO koko")
node.query("GRANT CREATE NAMED COLLECTION ON * TO koko")
assert (
"GRANT SELECT ON *.* TO koko\nGRANT CREATE NAMED COLLECTION ON * TO koko"
== node.query("SHOW GRANTS FOR koko;").strip()
)
node.query("DROP USER IF EXISTS koko")
node.query("CREATE USER koko")
node.query("GRANT CREATE NAMED COLLECTION ON * TO koko")
node.query("GRANT select ON *.* TO koko")
assert (
"GRANT SELECT ON *.* TO koko\nGRANT CREATE NAMED COLLECTION ON * TO koko"
== node.query("SHOW GRANTS FOR koko;").strip()
)
node.query("DROP USER IF EXISTS koko")
node.query("CREATE USER koko")
node.query("GRANT CREATE NAMED COLLECTION ON * TO koko")
node.query("GRANT select ON * TO koko")
assert (
"GRANT CREATE NAMED COLLECTION ON * TO koko\nGRANT SELECT ON default.* TO koko"
== node.query("SHOW GRANTS FOR koko;").strip()
)
node.query("DROP USER IF EXISTS koko")
node.query("CREATE USER koko")
node.query("GRANT select ON * TO koko")
node.query("GRANT CREATE NAMED COLLECTION ON * TO koko")
assert (
"GRANT CREATE NAMED COLLECTION ON * TO koko\nGRANT SELECT ON default.* TO koko"
== node.query("SHOW GRANTS FOR koko;").strip()
)
def test_granular_access_create_alter_drop_query(cluster):
node = cluster.instances["node"]
node.query("DROP USER IF EXISTS kek")
node.query("CREATE USER kek")
node.query("GRANT select ON *.* TO kek")
assert 0 == int(
node.query("SELECT count() FROM system.named_collections", user="kek")
)
assert (
"DB::Exception: kek: Not enough privileges. To execute this query it's necessary to have grant CREATE NAMED COLLECTION"
in node.query_and_get_error(
"CREATE NAMED COLLECTION collection2 AS key1=1, key2='value2'", user="kek"
)
)
node.query("GRANT create named collection ON collection2 TO kek")
node.query(
"CREATE NAMED COLLECTION collection2 AS key1=1, key2='value2'", user="kek"
)
assert 0 == int(
node.query("select count() from system.named_collections", user="kek")
)
node.query("GRANT show named collections ON collection2 TO kek")
assert (
"collection2"
== node.query("select name from system.named_collections", user="kek").strip()
)
assert (
"1"
== node.query(
"select collection['key1'] from system.named_collections where name = 'collection2'"
).strip()
)
assert (
"DB::Exception: kek: Not enough privileges. To execute this query it's necessary to have grant ALTER NAMED COLLECTION"
in node.query_and_get_error(
"ALTER NAMED COLLECTION collection2 SET key1=2", user="kek"
)
)
node.query("GRANT alter named collection ON collection2 TO kek")
node.query("ALTER NAMED COLLECTION collection2 SET key1=2", user="kek")
assert (
"2"
== node.query(
"select collection['key1'] from system.named_collections where name = 'collection2'"
).strip()
)
node.query("REVOKE alter named collection ON collection2 FROM kek")
assert (
"DB::Exception: kek: Not enough privileges. To execute this query it's necessary to have grant ALTER NAMED COLLECTION"
in node.query_and_get_error(
"ALTER NAMED COLLECTION collection2 SET key1=3", user="kek"
)
)
assert (
"DB::Exception: kek: Not enough privileges. To execute this query it's necessary to have grant DROP NAMED COLLECTION"
in node.query_and_get_error("DROP NAMED COLLECTION collection2", user="kek")
)
node.query("GRANT drop named collection ON collection2 TO kek")
node.query("DROP NAMED COLLECTION collection2", user="kek")
assert 0 == int(
node.query("select count() from system.named_collections", user="kek")
)
def test_config_reload(cluster):
node = cluster.instances["node"]
assert (
@ -164,6 +436,16 @@ def test_config_reload(cluster):
).strip()
)
replace_in_server_config(node, "value2", "value1")
node.query("SYSTEM RELOAD CONFIG")
assert (
"value1"
== node.query(
"select collection['key1'] from system.named_collections where name = 'collection1'"
).strip()
)
def test_sql_commands(cluster):
node = cluster.instances["node"]

View File

@ -4,7 +4,7 @@
<password></password>
<profile>default</profile>
<quota>default</quota>
<show_named_collections>1</show_named_collections>
<named_collection_control>1</named_collection_control>
<show_named_collections_secrets>1</show_named_collections_secrets>
</default>
</users>

View File

@ -4,7 +4,7 @@
<password></password>
<profile>default</profile>
<quota>default</quota>
<show_named_collections>1</show_named_collections>
<named_collection_control>1</named_collection_control>
<show_named_collections_secrets>1</show_named_collections_secrets>
</default>
</users>

View File

@ -39,7 +39,7 @@ ALTER MOVE PARTITION ['ALTER MOVE PART','MOVE PARTITION','MOVE PART'] TABLE ALTE
ALTER FETCH PARTITION ['ALTER FETCH PART','FETCH PARTITION'] TABLE ALTER TABLE
ALTER FREEZE PARTITION ['FREEZE PARTITION','UNFREEZE'] TABLE ALTER TABLE
ALTER DATABASE SETTINGS ['ALTER DATABASE SETTING','ALTER MODIFY DATABASE SETTING','MODIFY DATABASE SETTING'] DATABASE ALTER DATABASE
ALTER NAMED COLLECTION [] \N ALTER
ALTER NAMED COLLECTION [] NAMED_COLLECTION NAMED COLLECTION CONTROL
ALTER TABLE [] \N ALTER
ALTER DATABASE [] \N ALTER
ALTER VIEW REFRESH ['ALTER LIVE VIEW REFRESH','REFRESH VIEW'] VIEW ALTER VIEW
@ -53,14 +53,14 @@ CREATE DICTIONARY [] DICTIONARY CREATE
CREATE TEMPORARY TABLE [] GLOBAL CREATE ARBITRARY TEMPORARY TABLE
CREATE ARBITRARY TEMPORARY TABLE [] GLOBAL CREATE
CREATE FUNCTION [] GLOBAL CREATE
CREATE NAMED COLLECTION [] GLOBAL CREATE
CREATE NAMED COLLECTION [] NAMED_COLLECTION NAMED COLLECTION CONTROL
CREATE [] \N ALL
DROP DATABASE [] DATABASE DROP
DROP TABLE [] TABLE DROP
DROP VIEW [] VIEW DROP
DROP DICTIONARY [] DICTIONARY DROP
DROP FUNCTION [] GLOBAL DROP
DROP NAMED COLLECTION [] GLOBAL DROP
DROP NAMED COLLECTION [] NAMED_COLLECTION NAMED COLLECTION CONTROL
DROP [] \N ALL
TRUNCATE ['TRUNCATE TABLE'] TABLE ALL
OPTIMIZE ['OPTIMIZE TABLE'] TABLE ALL
@ -90,9 +90,10 @@ SHOW ROW POLICIES ['SHOW POLICIES','SHOW CREATE ROW POLICY','SHOW CREATE POLICY'
SHOW QUOTAS ['SHOW CREATE QUOTA'] GLOBAL SHOW ACCESS
SHOW SETTINGS PROFILES ['SHOW PROFILES','SHOW CREATE SETTINGS PROFILE','SHOW CREATE PROFILE'] GLOBAL SHOW ACCESS
SHOW ACCESS [] \N ACCESS MANAGEMENT
SHOW NAMED COLLECTIONS ['SHOW NAMED COLLECTIONS'] GLOBAL ACCESS MANAGEMENT
SHOW NAMED COLLECTIONS SECRETS ['SHOW NAMED COLLECTIONS SECRETS'] GLOBAL ACCESS MANAGEMENT
ACCESS MANAGEMENT [] \N ALL
SHOW NAMED COLLECTIONS ['SHOW NAMED COLLECTIONS'] NAMED_COLLECTION NAMED COLLECTION CONTROL
SHOW NAMED COLLECTIONS SECRETS ['SHOW NAMED COLLECTIONS SECRETS'] NAMED_COLLECTION NAMED COLLECTION CONTROL
NAMED COLLECTION CONTROL [] NAMED_COLLECTION ALL
SYSTEM SHUTDOWN ['SYSTEM KILL','SHUTDOWN'] GLOBAL SYSTEM
SYSTEM DROP DNS CACHE ['SYSTEM DROP DNS','DROP DNS CACHE','DROP DNS'] GLOBAL SYSTEM DROP CACHE
SYSTEM DROP MARK CACHE ['SYSTEM DROP MARK','DROP MARK CACHE','DROP MARKS'] GLOBAL SYSTEM DROP CACHE

View File

@ -289,7 +289,7 @@ CREATE TABLE system.grants
(
`user_name` Nullable(String),
`role_name` Nullable(String),
`access_type` Enum16('SHOW DATABASES' = 0, 'SHOW TABLES' = 1, 'SHOW COLUMNS' = 2, 'SHOW DICTIONARIES' = 3, 'SHOW' = 4, 'SHOW FILESYSTEM CACHES' = 5, 'SELECT' = 6, 'INSERT' = 7, 'ALTER UPDATE' = 8, 'ALTER DELETE' = 9, 'ALTER ADD COLUMN' = 10, 'ALTER MODIFY COLUMN' = 11, 'ALTER DROP COLUMN' = 12, 'ALTER COMMENT COLUMN' = 13, 'ALTER CLEAR COLUMN' = 14, 'ALTER RENAME COLUMN' = 15, 'ALTER MATERIALIZE COLUMN' = 16, 'ALTER COLUMN' = 17, 'ALTER MODIFY COMMENT' = 18, 'ALTER ORDER BY' = 19, 'ALTER SAMPLE BY' = 20, 'ALTER ADD INDEX' = 21, 'ALTER DROP INDEX' = 22, 'ALTER MATERIALIZE INDEX' = 23, 'ALTER CLEAR INDEX' = 24, 'ALTER INDEX' = 25, 'ALTER ADD PROJECTION' = 26, 'ALTER DROP PROJECTION' = 27, 'ALTER MATERIALIZE PROJECTION' = 28, 'ALTER CLEAR PROJECTION' = 29, 'ALTER PROJECTION' = 30, 'ALTER ADD CONSTRAINT' = 31, 'ALTER DROP CONSTRAINT' = 32, 'ALTER CONSTRAINT' = 33, 'ALTER TTL' = 34, 'ALTER MATERIALIZE TTL' = 35, 'ALTER SETTINGS' = 36, 'ALTER MOVE PARTITION' = 37, 'ALTER FETCH PARTITION' = 38, 'ALTER FREEZE PARTITION' = 39, 'ALTER DATABASE SETTINGS' = 40, 'ALTER NAMED COLLECTION' = 41, 'ALTER TABLE' = 42, 'ALTER DATABASE' = 43, 'ALTER VIEW REFRESH' = 44, 'ALTER VIEW MODIFY QUERY' = 45, 'ALTER VIEW' = 46, 'ALTER' = 47, 'CREATE DATABASE' = 48, 'CREATE TABLE' = 49, 'CREATE VIEW' = 50, 'CREATE DICTIONARY' = 51, 'CREATE TEMPORARY TABLE' = 52, 'CREATE ARBITRARY TEMPORARY TABLE' = 53, 'CREATE FUNCTION' = 54, 'CREATE NAMED COLLECTION' = 55, 'CREATE' = 56, 'DROP DATABASE' = 57, 'DROP TABLE' = 58, 'DROP VIEW' = 59, 'DROP DICTIONARY' = 60, 'DROP FUNCTION' = 61, 'DROP NAMED COLLECTION' = 62, 'DROP' = 63, 'TRUNCATE' = 64, 'OPTIMIZE' = 65, 'BACKUP' = 66, 'KILL QUERY' = 67, 'KILL TRANSACTION' = 68, 'MOVE PARTITION BETWEEN SHARDS' = 69, 'CREATE USER' = 70, 'ALTER USER' = 71, 'DROP USER' = 72, 'CREATE ROLE' = 73, 'ALTER ROLE' = 74, 'DROP ROLE' = 75, 'ROLE ADMIN' = 76, 'CREATE ROW POLICY' = 77, 'ALTER ROW POLICY' = 78, 'DROP ROW POLICY' = 79, 'CREATE QUOTA' = 80, 'ALTER QUOTA' = 81, 'DROP QUOTA' = 82, 'CREATE SETTINGS PROFILE' = 83, 'ALTER SETTINGS PROFILE' = 84, 'DROP SETTINGS PROFILE' = 85, 'SHOW USERS' = 86, 'SHOW ROLES' = 87, 'SHOW ROW POLICIES' = 88, 'SHOW QUOTAS' = 89, 'SHOW SETTINGS PROFILES' = 90, 'SHOW ACCESS' = 91, 'SHOW NAMED COLLECTIONS' = 92, 'SHOW NAMED COLLECTIONS SECRETS' = 93, 'ACCESS MANAGEMENT' = 94, 'SYSTEM SHUTDOWN' = 95, 'SYSTEM DROP DNS CACHE' = 96, 'SYSTEM DROP MARK CACHE' = 97, 'SYSTEM DROP UNCOMPRESSED CACHE' = 98, 'SYSTEM DROP MMAP CACHE' = 99, 'SYSTEM DROP QUERY CACHE' = 100, 'SYSTEM DROP COMPILED EXPRESSION CACHE' = 101, 'SYSTEM DROP FILESYSTEM CACHE' = 102, 'SYSTEM DROP SCHEMA CACHE' = 103, 'SYSTEM DROP S3 CLIENT CACHE' = 104, 'SYSTEM DROP CACHE' = 105, 'SYSTEM RELOAD CONFIG' = 106, 'SYSTEM RELOAD USERS' = 107, 'SYSTEM RELOAD SYMBOLS' = 108, 'SYSTEM RELOAD DICTIONARY' = 109, 'SYSTEM RELOAD MODEL' = 110, 'SYSTEM RELOAD FUNCTION' = 111, 'SYSTEM RELOAD EMBEDDED DICTIONARIES' = 112, 'SYSTEM RELOAD' = 113, 'SYSTEM RESTART DISK' = 114, 'SYSTEM MERGES' = 115, 'SYSTEM TTL MERGES' = 116, 'SYSTEM FETCHES' = 117, 'SYSTEM MOVES' = 118, 'SYSTEM DISTRIBUTED SENDS' = 119, 'SYSTEM REPLICATED SENDS' = 120, 'SYSTEM SENDS' = 121, 'SYSTEM REPLICATION QUEUES' = 122, 'SYSTEM DROP REPLICA' = 123, 'SYSTEM SYNC REPLICA' = 124, 'SYSTEM RESTART REPLICA' = 125, 'SYSTEM RESTORE REPLICA' = 126, 'SYSTEM WAIT LOADING PARTS' = 127, 'SYSTEM SYNC DATABASE REPLICA' = 128, 'SYSTEM SYNC TRANSACTION LOG' = 129, 'SYSTEM SYNC FILE CACHE' = 130, 'SYSTEM FLUSH DISTRIBUTED' = 131, 'SYSTEM FLUSH LOGS' = 132, 'SYSTEM FLUSH' = 133, 'SYSTEM THREAD FUZZER' = 134, 'SYSTEM UNFREEZE' = 135, 'SYSTEM' = 136, 'dictGet' = 137, 'addressToLine' = 138, 'addressToLineWithInlines' = 139, 'addressToSymbol' = 140, 'demangle' = 141, 'INTROSPECTION' = 142, 'FILE' = 143, 'URL' = 144, 'REMOTE' = 145, 'MONGO' = 146, 'MEILISEARCH' = 147, 'MYSQL' = 148, 'POSTGRES' = 149, 'SQLITE' = 150, 'ODBC' = 151, 'JDBC' = 152, 'HDFS' = 153, 'S3' = 154, 'HIVE' = 155, 'SOURCES' = 156, 'CLUSTER' = 157, 'ALL' = 158, 'NONE' = 159),
`access_type` Enum16('SHOW DATABASES' = 0, 'SHOW TABLES' = 1, 'SHOW COLUMNS' = 2, 'SHOW DICTIONARIES' = 3, 'SHOW' = 4, 'SHOW FILESYSTEM CACHES' = 5, 'SELECT' = 6, 'INSERT' = 7, 'ALTER UPDATE' = 8, 'ALTER DELETE' = 9, 'ALTER ADD COLUMN' = 10, 'ALTER MODIFY COLUMN' = 11, 'ALTER DROP COLUMN' = 12, 'ALTER COMMENT COLUMN' = 13, 'ALTER CLEAR COLUMN' = 14, 'ALTER RENAME COLUMN' = 15, 'ALTER MATERIALIZE COLUMN' = 16, 'ALTER COLUMN' = 17, 'ALTER MODIFY COMMENT' = 18, 'ALTER ORDER BY' = 19, 'ALTER SAMPLE BY' = 20, 'ALTER ADD INDEX' = 21, 'ALTER DROP INDEX' = 22, 'ALTER MATERIALIZE INDEX' = 23, 'ALTER CLEAR INDEX' = 24, 'ALTER INDEX' = 25, 'ALTER ADD PROJECTION' = 26, 'ALTER DROP PROJECTION' = 27, 'ALTER MATERIALIZE PROJECTION' = 28, 'ALTER CLEAR PROJECTION' = 29, 'ALTER PROJECTION' = 30, 'ALTER ADD CONSTRAINT' = 31, 'ALTER DROP CONSTRAINT' = 32, 'ALTER CONSTRAINT' = 33, 'ALTER TTL' = 34, 'ALTER MATERIALIZE TTL' = 35, 'ALTER SETTINGS' = 36, 'ALTER MOVE PARTITION' = 37, 'ALTER FETCH PARTITION' = 38, 'ALTER FREEZE PARTITION' = 39, 'ALTER DATABASE SETTINGS' = 40, 'ALTER NAMED COLLECTION' = 41, 'ALTER TABLE' = 42, 'ALTER DATABASE' = 43, 'ALTER VIEW REFRESH' = 44, 'ALTER VIEW MODIFY QUERY' = 45, 'ALTER VIEW' = 46, 'ALTER' = 47, 'CREATE DATABASE' = 48, 'CREATE TABLE' = 49, 'CREATE VIEW' = 50, 'CREATE DICTIONARY' = 51, 'CREATE TEMPORARY TABLE' = 52, 'CREATE ARBITRARY TEMPORARY TABLE' = 53, 'CREATE FUNCTION' = 54, 'CREATE NAMED COLLECTION' = 55, 'CREATE' = 56, 'DROP DATABASE' = 57, 'DROP TABLE' = 58, 'DROP VIEW' = 59, 'DROP DICTIONARY' = 60, 'DROP FUNCTION' = 61, 'DROP NAMED COLLECTION' = 62, 'DROP' = 63, 'TRUNCATE' = 64, 'OPTIMIZE' = 65, 'BACKUP' = 66, 'KILL QUERY' = 67, 'KILL TRANSACTION' = 68, 'MOVE PARTITION BETWEEN SHARDS' = 69, 'CREATE USER' = 70, 'ALTER USER' = 71, 'DROP USER' = 72, 'CREATE ROLE' = 73, 'ALTER ROLE' = 74, 'DROP ROLE' = 75, 'ROLE ADMIN' = 76, 'CREATE ROW POLICY' = 77, 'ALTER ROW POLICY' = 78, 'DROP ROW POLICY' = 79, 'CREATE QUOTA' = 80, 'ALTER QUOTA' = 81, 'DROP QUOTA' = 82, 'CREATE SETTINGS PROFILE' = 83, 'ALTER SETTINGS PROFILE' = 84, 'DROP SETTINGS PROFILE' = 85, 'SHOW USERS' = 86, 'SHOW ROLES' = 87, 'SHOW ROW POLICIES' = 88, 'SHOW QUOTAS' = 89, 'SHOW SETTINGS PROFILES' = 90, 'SHOW ACCESS' = 91, 'ACCESS MANAGEMENT' = 92, 'SHOW NAMED COLLECTIONS' = 93, 'SHOW NAMED COLLECTIONS SECRETS' = 94, 'NAMED COLLECTION CONTROL' = 95, 'SYSTEM SHUTDOWN' = 96, 'SYSTEM DROP DNS CACHE' = 97, 'SYSTEM DROP MARK CACHE' = 98, 'SYSTEM DROP UNCOMPRESSED CACHE' = 99, 'SYSTEM DROP MMAP CACHE' = 100, 'SYSTEM DROP QUERY CACHE' = 101, 'SYSTEM DROP COMPILED EXPRESSION CACHE' = 102, 'SYSTEM DROP FILESYSTEM CACHE' = 103, 'SYSTEM DROP SCHEMA CACHE' = 104, 'SYSTEM DROP S3 CLIENT CACHE' = 105, 'SYSTEM DROP CACHE' = 106, 'SYSTEM RELOAD CONFIG' = 107, 'SYSTEM RELOAD USERS' = 108, 'SYSTEM RELOAD SYMBOLS' = 109, 'SYSTEM RELOAD DICTIONARY' = 110, 'SYSTEM RELOAD MODEL' = 111, 'SYSTEM RELOAD FUNCTION' = 112, 'SYSTEM RELOAD EMBEDDED DICTIONARIES' = 113, 'SYSTEM RELOAD' = 114, 'SYSTEM RESTART DISK' = 115, 'SYSTEM MERGES' = 116, 'SYSTEM TTL MERGES' = 117, 'SYSTEM FETCHES' = 118, 'SYSTEM MOVES' = 119, 'SYSTEM DISTRIBUTED SENDS' = 120, 'SYSTEM REPLICATED SENDS' = 121, 'SYSTEM SENDS' = 122, 'SYSTEM REPLICATION QUEUES' = 123, 'SYSTEM DROP REPLICA' = 124, 'SYSTEM SYNC REPLICA' = 125, 'SYSTEM RESTART REPLICA' = 126, 'SYSTEM RESTORE REPLICA' = 127, 'SYSTEM WAIT LOADING PARTS' = 128, 'SYSTEM SYNC DATABASE REPLICA' = 129, 'SYSTEM SYNC TRANSACTION LOG' = 130, 'SYSTEM SYNC FILE CACHE' = 131, 'SYSTEM FLUSH DISTRIBUTED' = 132, 'SYSTEM FLUSH LOGS' = 133, 'SYSTEM FLUSH' = 134, 'SYSTEM THREAD FUZZER' = 135, 'SYSTEM UNFREEZE' = 136, 'SYSTEM' = 137, 'dictGet' = 138, 'addressToLine' = 139, 'addressToLineWithInlines' = 140, 'addressToSymbol' = 141, 'demangle' = 142, 'INTROSPECTION' = 143, 'FILE' = 144, 'URL' = 145, 'REMOTE' = 146, 'MONGO' = 147, 'MEILISEARCH' = 148, 'MYSQL' = 149, 'POSTGRES' = 150, 'SQLITE' = 151, 'ODBC' = 152, 'JDBC' = 153, 'HDFS' = 154, 'S3' = 155, 'HIVE' = 156, 'SOURCES' = 157, 'CLUSTER' = 158, 'ALL' = 159, 'NONE' = 160),
`database` Nullable(String),
`table` Nullable(String),
`column` Nullable(String),
@ -570,10 +570,10 @@ ENGINE = SystemPartsColumns
COMMENT 'SYSTEM TABLE is built on the fly.'
CREATE TABLE system.privileges
(
`privilege` Enum16('SHOW DATABASES' = 0, 'SHOW TABLES' = 1, 'SHOW COLUMNS' = 2, 'SHOW DICTIONARIES' = 3, 'SHOW' = 4, 'SHOW FILESYSTEM CACHES' = 5, 'SELECT' = 6, 'INSERT' = 7, 'ALTER UPDATE' = 8, 'ALTER DELETE' = 9, 'ALTER ADD COLUMN' = 10, 'ALTER MODIFY COLUMN' = 11, 'ALTER DROP COLUMN' = 12, 'ALTER COMMENT COLUMN' = 13, 'ALTER CLEAR COLUMN' = 14, 'ALTER RENAME COLUMN' = 15, 'ALTER MATERIALIZE COLUMN' = 16, 'ALTER COLUMN' = 17, 'ALTER MODIFY COMMENT' = 18, 'ALTER ORDER BY' = 19, 'ALTER SAMPLE BY' = 20, 'ALTER ADD INDEX' = 21, 'ALTER DROP INDEX' = 22, 'ALTER MATERIALIZE INDEX' = 23, 'ALTER CLEAR INDEX' = 24, 'ALTER INDEX' = 25, 'ALTER ADD PROJECTION' = 26, 'ALTER DROP PROJECTION' = 27, 'ALTER MATERIALIZE PROJECTION' = 28, 'ALTER CLEAR PROJECTION' = 29, 'ALTER PROJECTION' = 30, 'ALTER ADD CONSTRAINT' = 31, 'ALTER DROP CONSTRAINT' = 32, 'ALTER CONSTRAINT' = 33, 'ALTER TTL' = 34, 'ALTER MATERIALIZE TTL' = 35, 'ALTER SETTINGS' = 36, 'ALTER MOVE PARTITION' = 37, 'ALTER FETCH PARTITION' = 38, 'ALTER FREEZE PARTITION' = 39, 'ALTER DATABASE SETTINGS' = 40, 'ALTER NAMED COLLECTION' = 41, 'ALTER TABLE' = 42, 'ALTER DATABASE' = 43, 'ALTER VIEW REFRESH' = 44, 'ALTER VIEW MODIFY QUERY' = 45, 'ALTER VIEW' = 46, 'ALTER' = 47, 'CREATE DATABASE' = 48, 'CREATE TABLE' = 49, 'CREATE VIEW' = 50, 'CREATE DICTIONARY' = 51, 'CREATE TEMPORARY TABLE' = 52, 'CREATE ARBITRARY TEMPORARY TABLE' = 53, 'CREATE FUNCTION' = 54, 'CREATE NAMED COLLECTION' = 55, 'CREATE' = 56, 'DROP DATABASE' = 57, 'DROP TABLE' = 58, 'DROP VIEW' = 59, 'DROP DICTIONARY' = 60, 'DROP FUNCTION' = 61, 'DROP NAMED COLLECTION' = 62, 'DROP' = 63, 'TRUNCATE' = 64, 'OPTIMIZE' = 65, 'BACKUP' = 66, 'KILL QUERY' = 67, 'KILL TRANSACTION' = 68, 'MOVE PARTITION BETWEEN SHARDS' = 69, 'CREATE USER' = 70, 'ALTER USER' = 71, 'DROP USER' = 72, 'CREATE ROLE' = 73, 'ALTER ROLE' = 74, 'DROP ROLE' = 75, 'ROLE ADMIN' = 76, 'CREATE ROW POLICY' = 77, 'ALTER ROW POLICY' = 78, 'DROP ROW POLICY' = 79, 'CREATE QUOTA' = 80, 'ALTER QUOTA' = 81, 'DROP QUOTA' = 82, 'CREATE SETTINGS PROFILE' = 83, 'ALTER SETTINGS PROFILE' = 84, 'DROP SETTINGS PROFILE' = 85, 'SHOW USERS' = 86, 'SHOW ROLES' = 87, 'SHOW ROW POLICIES' = 88, 'SHOW QUOTAS' = 89, 'SHOW SETTINGS PROFILES' = 90, 'SHOW ACCESS' = 91, 'SHOW NAMED COLLECTIONS' = 92, 'SHOW NAMED COLLECTIONS SECRETS' = 93, 'ACCESS MANAGEMENT' = 94, 'SYSTEM SHUTDOWN' = 95, 'SYSTEM DROP DNS CACHE' = 96, 'SYSTEM DROP MARK CACHE' = 97, 'SYSTEM DROP UNCOMPRESSED CACHE' = 98, 'SYSTEM DROP MMAP CACHE' = 99, 'SYSTEM DROP QUERY CACHE' = 100, 'SYSTEM DROP COMPILED EXPRESSION CACHE' = 101, 'SYSTEM DROP FILESYSTEM CACHE' = 102, 'SYSTEM DROP SCHEMA CACHE' = 103, 'SYSTEM DROP S3 CLIENT CACHE' = 104, 'SYSTEM DROP CACHE' = 105, 'SYSTEM RELOAD CONFIG' = 106, 'SYSTEM RELOAD USERS' = 107, 'SYSTEM RELOAD SYMBOLS' = 108, 'SYSTEM RELOAD DICTIONARY' = 109, 'SYSTEM RELOAD MODEL' = 110, 'SYSTEM RELOAD FUNCTION' = 111, 'SYSTEM RELOAD EMBEDDED DICTIONARIES' = 112, 'SYSTEM RELOAD' = 113, 'SYSTEM RESTART DISK' = 114, 'SYSTEM MERGES' = 115, 'SYSTEM TTL MERGES' = 116, 'SYSTEM FETCHES' = 117, 'SYSTEM MOVES' = 118, 'SYSTEM DISTRIBUTED SENDS' = 119, 'SYSTEM REPLICATED SENDS' = 120, 'SYSTEM SENDS' = 121, 'SYSTEM REPLICATION QUEUES' = 122, 'SYSTEM DROP REPLICA' = 123, 'SYSTEM SYNC REPLICA' = 124, 'SYSTEM RESTART REPLICA' = 125, 'SYSTEM RESTORE REPLICA' = 126, 'SYSTEM WAIT LOADING PARTS' = 127, 'SYSTEM SYNC DATABASE REPLICA' = 128, 'SYSTEM SYNC TRANSACTION LOG' = 129, 'SYSTEM SYNC FILE CACHE' = 130, 'SYSTEM FLUSH DISTRIBUTED' = 131, 'SYSTEM FLUSH LOGS' = 132, 'SYSTEM FLUSH' = 133, 'SYSTEM THREAD FUZZER' = 134, 'SYSTEM UNFREEZE' = 135, 'SYSTEM' = 136, 'dictGet' = 137, 'addressToLine' = 138, 'addressToLineWithInlines' = 139, 'addressToSymbol' = 140, 'demangle' = 141, 'INTROSPECTION' = 142, 'FILE' = 143, 'URL' = 144, 'REMOTE' = 145, 'MONGO' = 146, 'MEILISEARCH' = 147, 'MYSQL' = 148, 'POSTGRES' = 149, 'SQLITE' = 150, 'ODBC' = 151, 'JDBC' = 152, 'HDFS' = 153, 'S3' = 154, 'HIVE' = 155, 'SOURCES' = 156, 'CLUSTER' = 157, 'ALL' = 158, 'NONE' = 159),
`privilege` Enum16('SHOW DATABASES' = 0, 'SHOW TABLES' = 1, 'SHOW COLUMNS' = 2, 'SHOW DICTIONARIES' = 3, 'SHOW' = 4, 'SHOW FILESYSTEM CACHES' = 5, 'SELECT' = 6, 'INSERT' = 7, 'ALTER UPDATE' = 8, 'ALTER DELETE' = 9, 'ALTER ADD COLUMN' = 10, 'ALTER MODIFY COLUMN' = 11, 'ALTER DROP COLUMN' = 12, 'ALTER COMMENT COLUMN' = 13, 'ALTER CLEAR COLUMN' = 14, 'ALTER RENAME COLUMN' = 15, 'ALTER MATERIALIZE COLUMN' = 16, 'ALTER COLUMN' = 17, 'ALTER MODIFY COMMENT' = 18, 'ALTER ORDER BY' = 19, 'ALTER SAMPLE BY' = 20, 'ALTER ADD INDEX' = 21, 'ALTER DROP INDEX' = 22, 'ALTER MATERIALIZE INDEX' = 23, 'ALTER CLEAR INDEX' = 24, 'ALTER INDEX' = 25, 'ALTER ADD PROJECTION' = 26, 'ALTER DROP PROJECTION' = 27, 'ALTER MATERIALIZE PROJECTION' = 28, 'ALTER CLEAR PROJECTION' = 29, 'ALTER PROJECTION' = 30, 'ALTER ADD CONSTRAINT' = 31, 'ALTER DROP CONSTRAINT' = 32, 'ALTER CONSTRAINT' = 33, 'ALTER TTL' = 34, 'ALTER MATERIALIZE TTL' = 35, 'ALTER SETTINGS' = 36, 'ALTER MOVE PARTITION' = 37, 'ALTER FETCH PARTITION' = 38, 'ALTER FREEZE PARTITION' = 39, 'ALTER DATABASE SETTINGS' = 40, 'ALTER NAMED COLLECTION' = 41, 'ALTER TABLE' = 42, 'ALTER DATABASE' = 43, 'ALTER VIEW REFRESH' = 44, 'ALTER VIEW MODIFY QUERY' = 45, 'ALTER VIEW' = 46, 'ALTER' = 47, 'CREATE DATABASE' = 48, 'CREATE TABLE' = 49, 'CREATE VIEW' = 50, 'CREATE DICTIONARY' = 51, 'CREATE TEMPORARY TABLE' = 52, 'CREATE ARBITRARY TEMPORARY TABLE' = 53, 'CREATE FUNCTION' = 54, 'CREATE NAMED COLLECTION' = 55, 'CREATE' = 56, 'DROP DATABASE' = 57, 'DROP TABLE' = 58, 'DROP VIEW' = 59, 'DROP DICTIONARY' = 60, 'DROP FUNCTION' = 61, 'DROP NAMED COLLECTION' = 62, 'DROP' = 63, 'TRUNCATE' = 64, 'OPTIMIZE' = 65, 'BACKUP' = 66, 'KILL QUERY' = 67, 'KILL TRANSACTION' = 68, 'MOVE PARTITION BETWEEN SHARDS' = 69, 'CREATE USER' = 70, 'ALTER USER' = 71, 'DROP USER' = 72, 'CREATE ROLE' = 73, 'ALTER ROLE' = 74, 'DROP ROLE' = 75, 'ROLE ADMIN' = 76, 'CREATE ROW POLICY' = 77, 'ALTER ROW POLICY' = 78, 'DROP ROW POLICY' = 79, 'CREATE QUOTA' = 80, 'ALTER QUOTA' = 81, 'DROP QUOTA' = 82, 'CREATE SETTINGS PROFILE' = 83, 'ALTER SETTINGS PROFILE' = 84, 'DROP SETTINGS PROFILE' = 85, 'SHOW USERS' = 86, 'SHOW ROLES' = 87, 'SHOW ROW POLICIES' = 88, 'SHOW QUOTAS' = 89, 'SHOW SETTINGS PROFILES' = 90, 'SHOW ACCESS' = 91, 'ACCESS MANAGEMENT' = 92, 'SHOW NAMED COLLECTIONS' = 93, 'SHOW NAMED COLLECTIONS SECRETS' = 94, 'NAMED COLLECTION CONTROL' = 95, 'SYSTEM SHUTDOWN' = 96, 'SYSTEM DROP DNS CACHE' = 97, 'SYSTEM DROP MARK CACHE' = 98, 'SYSTEM DROP UNCOMPRESSED CACHE' = 99, 'SYSTEM DROP MMAP CACHE' = 100, 'SYSTEM DROP QUERY CACHE' = 101, 'SYSTEM DROP COMPILED EXPRESSION CACHE' = 102, 'SYSTEM DROP FILESYSTEM CACHE' = 103, 'SYSTEM DROP SCHEMA CACHE' = 104, 'SYSTEM DROP S3 CLIENT CACHE' = 105, 'SYSTEM DROP CACHE' = 106, 'SYSTEM RELOAD CONFIG' = 107, 'SYSTEM RELOAD USERS' = 108, 'SYSTEM RELOAD SYMBOLS' = 109, 'SYSTEM RELOAD DICTIONARY' = 110, 'SYSTEM RELOAD MODEL' = 111, 'SYSTEM RELOAD FUNCTION' = 112, 'SYSTEM RELOAD EMBEDDED DICTIONARIES' = 113, 'SYSTEM RELOAD' = 114, 'SYSTEM RESTART DISK' = 115, 'SYSTEM MERGES' = 116, 'SYSTEM TTL MERGES' = 117, 'SYSTEM FETCHES' = 118, 'SYSTEM MOVES' = 119, 'SYSTEM DISTRIBUTED SENDS' = 120, 'SYSTEM REPLICATED SENDS' = 121, 'SYSTEM SENDS' = 122, 'SYSTEM REPLICATION QUEUES' = 123, 'SYSTEM DROP REPLICA' = 124, 'SYSTEM SYNC REPLICA' = 125, 'SYSTEM RESTART REPLICA' = 126, 'SYSTEM RESTORE REPLICA' = 127, 'SYSTEM WAIT LOADING PARTS' = 128, 'SYSTEM SYNC DATABASE REPLICA' = 129, 'SYSTEM SYNC TRANSACTION LOG' = 130, 'SYSTEM SYNC FILE CACHE' = 131, 'SYSTEM FLUSH DISTRIBUTED' = 132, 'SYSTEM FLUSH LOGS' = 133, 'SYSTEM FLUSH' = 134, 'SYSTEM THREAD FUZZER' = 135, 'SYSTEM UNFREEZE' = 136, 'SYSTEM' = 137, 'dictGet' = 138, 'addressToLine' = 139, 'addressToLineWithInlines' = 140, 'addressToSymbol' = 141, 'demangle' = 142, 'INTROSPECTION' = 143, 'FILE' = 144, 'URL' = 145, 'REMOTE' = 146, 'MONGO' = 147, 'MEILISEARCH' = 148, 'MYSQL' = 149, 'POSTGRES' = 150, 'SQLITE' = 151, 'ODBC' = 152, 'JDBC' = 153, 'HDFS' = 154, 'S3' = 155, 'HIVE' = 156, 'SOURCES' = 157, 'CLUSTER' = 158, 'ALL' = 159, 'NONE' = 160),
`aliases` Array(String),
`level` Nullable(Enum8('GLOBAL' = 0, 'DATABASE' = 1, 'TABLE' = 2, 'DICTIONARY' = 3, 'VIEW' = 4, 'COLUMN' = 5)),
`parent_group` Nullable(Enum16('SHOW DATABASES' = 0, 'SHOW TABLES' = 1, 'SHOW COLUMNS' = 2, 'SHOW DICTIONARIES' = 3, 'SHOW' = 4, 'SHOW FILESYSTEM CACHES' = 5, 'SELECT' = 6, 'INSERT' = 7, 'ALTER UPDATE' = 8, 'ALTER DELETE' = 9, 'ALTER ADD COLUMN' = 10, 'ALTER MODIFY COLUMN' = 11, 'ALTER DROP COLUMN' = 12, 'ALTER COMMENT COLUMN' = 13, 'ALTER CLEAR COLUMN' = 14, 'ALTER RENAME COLUMN' = 15, 'ALTER MATERIALIZE COLUMN' = 16, 'ALTER COLUMN' = 17, 'ALTER MODIFY COMMENT' = 18, 'ALTER ORDER BY' = 19, 'ALTER SAMPLE BY' = 20, 'ALTER ADD INDEX' = 21, 'ALTER DROP INDEX' = 22, 'ALTER MATERIALIZE INDEX' = 23, 'ALTER CLEAR INDEX' = 24, 'ALTER INDEX' = 25, 'ALTER ADD PROJECTION' = 26, 'ALTER DROP PROJECTION' = 27, 'ALTER MATERIALIZE PROJECTION' = 28, 'ALTER CLEAR PROJECTION' = 29, 'ALTER PROJECTION' = 30, 'ALTER ADD CONSTRAINT' = 31, 'ALTER DROP CONSTRAINT' = 32, 'ALTER CONSTRAINT' = 33, 'ALTER TTL' = 34, 'ALTER MATERIALIZE TTL' = 35, 'ALTER SETTINGS' = 36, 'ALTER MOVE PARTITION' = 37, 'ALTER FETCH PARTITION' = 38, 'ALTER FREEZE PARTITION' = 39, 'ALTER DATABASE SETTINGS' = 40, 'ALTER NAMED COLLECTION' = 41, 'ALTER TABLE' = 42, 'ALTER DATABASE' = 43, 'ALTER VIEW REFRESH' = 44, 'ALTER VIEW MODIFY QUERY' = 45, 'ALTER VIEW' = 46, 'ALTER' = 47, 'CREATE DATABASE' = 48, 'CREATE TABLE' = 49, 'CREATE VIEW' = 50, 'CREATE DICTIONARY' = 51, 'CREATE TEMPORARY TABLE' = 52, 'CREATE ARBITRARY TEMPORARY TABLE' = 53, 'CREATE FUNCTION' = 54, 'CREATE NAMED COLLECTION' = 55, 'CREATE' = 56, 'DROP DATABASE' = 57, 'DROP TABLE' = 58, 'DROP VIEW' = 59, 'DROP DICTIONARY' = 60, 'DROP FUNCTION' = 61, 'DROP NAMED COLLECTION' = 62, 'DROP' = 63, 'TRUNCATE' = 64, 'OPTIMIZE' = 65, 'BACKUP' = 66, 'KILL QUERY' = 67, 'KILL TRANSACTION' = 68, 'MOVE PARTITION BETWEEN SHARDS' = 69, 'CREATE USER' = 70, 'ALTER USER' = 71, 'DROP USER' = 72, 'CREATE ROLE' = 73, 'ALTER ROLE' = 74, 'DROP ROLE' = 75, 'ROLE ADMIN' = 76, 'CREATE ROW POLICY' = 77, 'ALTER ROW POLICY' = 78, 'DROP ROW POLICY' = 79, 'CREATE QUOTA' = 80, 'ALTER QUOTA' = 81, 'DROP QUOTA' = 82, 'CREATE SETTINGS PROFILE' = 83, 'ALTER SETTINGS PROFILE' = 84, 'DROP SETTINGS PROFILE' = 85, 'SHOW USERS' = 86, 'SHOW ROLES' = 87, 'SHOW ROW POLICIES' = 88, 'SHOW QUOTAS' = 89, 'SHOW SETTINGS PROFILES' = 90, 'SHOW ACCESS' = 91, 'SHOW NAMED COLLECTIONS' = 92, 'SHOW NAMED COLLECTIONS SECRETS' = 93, 'ACCESS MANAGEMENT' = 94, 'SYSTEM SHUTDOWN' = 95, 'SYSTEM DROP DNS CACHE' = 96, 'SYSTEM DROP MARK CACHE' = 97, 'SYSTEM DROP UNCOMPRESSED CACHE' = 98, 'SYSTEM DROP MMAP CACHE' = 99, 'SYSTEM DROP QUERY CACHE' = 100, 'SYSTEM DROP COMPILED EXPRESSION CACHE' = 101, 'SYSTEM DROP FILESYSTEM CACHE' = 102, 'SYSTEM DROP SCHEMA CACHE' = 103, 'SYSTEM DROP S3 CLIENT CACHE' = 104, 'SYSTEM DROP CACHE' = 105, 'SYSTEM RELOAD CONFIG' = 106, 'SYSTEM RELOAD USERS' = 107, 'SYSTEM RELOAD SYMBOLS' = 108, 'SYSTEM RELOAD DICTIONARY' = 109, 'SYSTEM RELOAD MODEL' = 110, 'SYSTEM RELOAD FUNCTION' = 111, 'SYSTEM RELOAD EMBEDDED DICTIONARIES' = 112, 'SYSTEM RELOAD' = 113, 'SYSTEM RESTART DISK' = 114, 'SYSTEM MERGES' = 115, 'SYSTEM TTL MERGES' = 116, 'SYSTEM FETCHES' = 117, 'SYSTEM MOVES' = 118, 'SYSTEM DISTRIBUTED SENDS' = 119, 'SYSTEM REPLICATED SENDS' = 120, 'SYSTEM SENDS' = 121, 'SYSTEM REPLICATION QUEUES' = 122, 'SYSTEM DROP REPLICA' = 123, 'SYSTEM SYNC REPLICA' = 124, 'SYSTEM RESTART REPLICA' = 125, 'SYSTEM RESTORE REPLICA' = 126, 'SYSTEM WAIT LOADING PARTS' = 127, 'SYSTEM SYNC DATABASE REPLICA' = 128, 'SYSTEM SYNC TRANSACTION LOG' = 129, 'SYSTEM SYNC FILE CACHE' = 130, 'SYSTEM FLUSH DISTRIBUTED' = 131, 'SYSTEM FLUSH LOGS' = 132, 'SYSTEM FLUSH' = 133, 'SYSTEM THREAD FUZZER' = 134, 'SYSTEM UNFREEZE' = 135, 'SYSTEM' = 136, 'dictGet' = 137, 'addressToLine' = 138, 'addressToLineWithInlines' = 139, 'addressToSymbol' = 140, 'demangle' = 141, 'INTROSPECTION' = 142, 'FILE' = 143, 'URL' = 144, 'REMOTE' = 145, 'MONGO' = 146, 'MEILISEARCH' = 147, 'MYSQL' = 148, 'POSTGRES' = 149, 'SQLITE' = 150, 'ODBC' = 151, 'JDBC' = 152, 'HDFS' = 153, 'S3' = 154, 'HIVE' = 155, 'SOURCES' = 156, 'CLUSTER' = 157, 'ALL' = 158, 'NONE' = 159))
`level` Nullable(Enum8('GLOBAL' = 0, 'DATABASE' = 1, 'TABLE' = 2, 'DICTIONARY' = 3, 'VIEW' = 4, 'COLUMN' = 5, 'NAMED_COLLECTION' = 6)),
`parent_group` Nullable(Enum16('SHOW DATABASES' = 0, 'SHOW TABLES' = 1, 'SHOW COLUMNS' = 2, 'SHOW DICTIONARIES' = 3, 'SHOW' = 4, 'SHOW FILESYSTEM CACHES' = 5, 'SELECT' = 6, 'INSERT' = 7, 'ALTER UPDATE' = 8, 'ALTER DELETE' = 9, 'ALTER ADD COLUMN' = 10, 'ALTER MODIFY COLUMN' = 11, 'ALTER DROP COLUMN' = 12, 'ALTER COMMENT COLUMN' = 13, 'ALTER CLEAR COLUMN' = 14, 'ALTER RENAME COLUMN' = 15, 'ALTER MATERIALIZE COLUMN' = 16, 'ALTER COLUMN' = 17, 'ALTER MODIFY COMMENT' = 18, 'ALTER ORDER BY' = 19, 'ALTER SAMPLE BY' = 20, 'ALTER ADD INDEX' = 21, 'ALTER DROP INDEX' = 22, 'ALTER MATERIALIZE INDEX' = 23, 'ALTER CLEAR INDEX' = 24, 'ALTER INDEX' = 25, 'ALTER ADD PROJECTION' = 26, 'ALTER DROP PROJECTION' = 27, 'ALTER MATERIALIZE PROJECTION' = 28, 'ALTER CLEAR PROJECTION' = 29, 'ALTER PROJECTION' = 30, 'ALTER ADD CONSTRAINT' = 31, 'ALTER DROP CONSTRAINT' = 32, 'ALTER CONSTRAINT' = 33, 'ALTER TTL' = 34, 'ALTER MATERIALIZE TTL' = 35, 'ALTER SETTINGS' = 36, 'ALTER MOVE PARTITION' = 37, 'ALTER FETCH PARTITION' = 38, 'ALTER FREEZE PARTITION' = 39, 'ALTER DATABASE SETTINGS' = 40, 'ALTER NAMED COLLECTION' = 41, 'ALTER TABLE' = 42, 'ALTER DATABASE' = 43, 'ALTER VIEW REFRESH' = 44, 'ALTER VIEW MODIFY QUERY' = 45, 'ALTER VIEW' = 46, 'ALTER' = 47, 'CREATE DATABASE' = 48, 'CREATE TABLE' = 49, 'CREATE VIEW' = 50, 'CREATE DICTIONARY' = 51, 'CREATE TEMPORARY TABLE' = 52, 'CREATE ARBITRARY TEMPORARY TABLE' = 53, 'CREATE FUNCTION' = 54, 'CREATE NAMED COLLECTION' = 55, 'CREATE' = 56, 'DROP DATABASE' = 57, 'DROP TABLE' = 58, 'DROP VIEW' = 59, 'DROP DICTIONARY' = 60, 'DROP FUNCTION' = 61, 'DROP NAMED COLLECTION' = 62, 'DROP' = 63, 'TRUNCATE' = 64, 'OPTIMIZE' = 65, 'BACKUP' = 66, 'KILL QUERY' = 67, 'KILL TRANSACTION' = 68, 'MOVE PARTITION BETWEEN SHARDS' = 69, 'CREATE USER' = 70, 'ALTER USER' = 71, 'DROP USER' = 72, 'CREATE ROLE' = 73, 'ALTER ROLE' = 74, 'DROP ROLE' = 75, 'ROLE ADMIN' = 76, 'CREATE ROW POLICY' = 77, 'ALTER ROW POLICY' = 78, 'DROP ROW POLICY' = 79, 'CREATE QUOTA' = 80, 'ALTER QUOTA' = 81, 'DROP QUOTA' = 82, 'CREATE SETTINGS PROFILE' = 83, 'ALTER SETTINGS PROFILE' = 84, 'DROP SETTINGS PROFILE' = 85, 'SHOW USERS' = 86, 'SHOW ROLES' = 87, 'SHOW ROW POLICIES' = 88, 'SHOW QUOTAS' = 89, 'SHOW SETTINGS PROFILES' = 90, 'SHOW ACCESS' = 91, 'ACCESS MANAGEMENT' = 92, 'SHOW NAMED COLLECTIONS' = 93, 'SHOW NAMED COLLECTIONS SECRETS' = 94, 'NAMED COLLECTION CONTROL' = 95, 'SYSTEM SHUTDOWN' = 96, 'SYSTEM DROP DNS CACHE' = 97, 'SYSTEM DROP MARK CACHE' = 98, 'SYSTEM DROP UNCOMPRESSED CACHE' = 99, 'SYSTEM DROP MMAP CACHE' = 100, 'SYSTEM DROP QUERY CACHE' = 101, 'SYSTEM DROP COMPILED EXPRESSION CACHE' = 102, 'SYSTEM DROP FILESYSTEM CACHE' = 103, 'SYSTEM DROP SCHEMA CACHE' = 104, 'SYSTEM DROP S3 CLIENT CACHE' = 105, 'SYSTEM DROP CACHE' = 106, 'SYSTEM RELOAD CONFIG' = 107, 'SYSTEM RELOAD USERS' = 108, 'SYSTEM RELOAD SYMBOLS' = 109, 'SYSTEM RELOAD DICTIONARY' = 110, 'SYSTEM RELOAD MODEL' = 111, 'SYSTEM RELOAD FUNCTION' = 112, 'SYSTEM RELOAD EMBEDDED DICTIONARIES' = 113, 'SYSTEM RELOAD' = 114, 'SYSTEM RESTART DISK' = 115, 'SYSTEM MERGES' = 116, 'SYSTEM TTL MERGES' = 117, 'SYSTEM FETCHES' = 118, 'SYSTEM MOVES' = 119, 'SYSTEM DISTRIBUTED SENDS' = 120, 'SYSTEM REPLICATED SENDS' = 121, 'SYSTEM SENDS' = 122, 'SYSTEM REPLICATION QUEUES' = 123, 'SYSTEM DROP REPLICA' = 124, 'SYSTEM SYNC REPLICA' = 125, 'SYSTEM RESTART REPLICA' = 126, 'SYSTEM RESTORE REPLICA' = 127, 'SYSTEM WAIT LOADING PARTS' = 128, 'SYSTEM SYNC DATABASE REPLICA' = 129, 'SYSTEM SYNC TRANSACTION LOG' = 130, 'SYSTEM SYNC FILE CACHE' = 131, 'SYSTEM FLUSH DISTRIBUTED' = 132, 'SYSTEM FLUSH LOGS' = 133, 'SYSTEM FLUSH' = 134, 'SYSTEM THREAD FUZZER' = 135, 'SYSTEM UNFREEZE' = 136, 'SYSTEM' = 137, 'dictGet' = 138, 'addressToLine' = 139, 'addressToLineWithInlines' = 140, 'addressToSymbol' = 141, 'demangle' = 142, 'INTROSPECTION' = 143, 'FILE' = 144, 'URL' = 145, 'REMOTE' = 146, 'MONGO' = 147, 'MEILISEARCH' = 148, 'MYSQL' = 149, 'POSTGRES' = 150, 'SQLITE' = 151, 'ODBC' = 152, 'JDBC' = 153, 'HDFS' = 154, 'S3' = 155, 'HIVE' = 156, 'SOURCES' = 157, 'CLUSTER' = 158, 'ALL' = 159, 'NONE' = 160))
)
ENGINE = SystemPrivileges
COMMENT 'SYSTEM TABLE is built on the fly.'