mirror of
https://github.com/ClickHouse/ClickHouse.git
synced 2024-11-22 07:31:57 +00:00
NamedCollections: prevent fields overriding in functions call
Add syntax in SQL and XML to mark specific fields to allow override or not. Also add a new setting to control the default behaviour when overriding support is not specified.
This commit is contained in:
parent
bc25839be8
commit
7d206cbc3c
@ -18,7 +18,15 @@ function, table engine, database, etc. In the examples below the parameter list
|
||||
linked to for each type.
|
||||
|
||||
Parameters set in a named collection can be overridden in SQL, this is shown in the examples
|
||||
below.
|
||||
below. This ability can be limited using `[NOT] OVERRIDABLE` keywords and XML attributes
|
||||
and/or the configuration option `allow_named_collection_override_by_default`.
|
||||
|
||||
:::warning
|
||||
If override is allowed, it may be possible for users without administrative access to
|
||||
figure out the credentials that you are trying to hide.
|
||||
If you are using named collections with that purpose, you should disable
|
||||
`allow_named_collection_override_by_default` (which is enabled by default).
|
||||
:::
|
||||
|
||||
## Storing named collections in the system database
|
||||
|
||||
@ -26,11 +34,17 @@ below.
|
||||
|
||||
```sql
|
||||
CREATE NAMED COLLECTION name AS
|
||||
key_1 = 'value',
|
||||
key_2 = 'value2',
|
||||
key_1 = 'value' OVERRIDABLE,
|
||||
key_2 = 'value2' NOT OVERRIDABLE,
|
||||
url = 'https://connection.url/'
|
||||
```
|
||||
|
||||
In the above example:
|
||||
|
||||
* `key_1` can always be overridden.
|
||||
* `key_2` can never be overridden.
|
||||
* `url` can be overridden or not depending on the value of `allow_named_collection_override_by_default`.
|
||||
|
||||
### Permissions to create named collections with DDL
|
||||
|
||||
To manage named collections with DDL a user must have the `named_control_collection` privilege. This can be assigned by adding a file to `/etc/clickhouse-server/users.d/`. The example gives the user `default` both the `access_management` and `named_collection_control` privileges:
|
||||
@ -61,25 +75,37 @@ In the above example the `password_sha256_hex` value is the hexadecimal represen
|
||||
<clickhouse>
|
||||
<named_collections>
|
||||
<name>
|
||||
<key_1>value</key_1>
|
||||
<key_2>value_2</key_2>
|
||||
<key_1 overridable="true">value</key_1>
|
||||
<key_2 overridable="false">value_2</key_2>
|
||||
<url>https://connection.url/</url>
|
||||
</name>
|
||||
</named_collections>
|
||||
</clickhouse>
|
||||
```
|
||||
|
||||
In the above example:
|
||||
|
||||
* `key_1` can always be overridden.
|
||||
* `key_2` can never be overridden.
|
||||
* `url` can be overridden or not depending on the value of `allow_named_collection_override_by_default`.
|
||||
|
||||
## Modifying named collections
|
||||
|
||||
Named collections that are created with DDL queries can be altered or dropped with DDL. Named collections created with XML files can be managed by editing or deleting the corresponding XML.
|
||||
|
||||
### Alter a DDL named collection
|
||||
|
||||
Change or add the keys `key1` and `key3` of the collection `collection2`:
|
||||
Change or add the keys `key1` and `key3` of the collection `collection2`
|
||||
(this will not change the value of the `overridable` flag for those keys):
|
||||
```sql
|
||||
ALTER NAMED COLLECTION collection2 SET key1=4, key3='value3'
|
||||
```
|
||||
|
||||
Change or add the key `key1` and allow it to be always overridden:
|
||||
```sql
|
||||
ALTER NAMED COLLECTION collection2 SET key1=4 OVERRIDABLE
|
||||
```
|
||||
|
||||
Remove the key `key2` from `collection2`:
|
||||
```sql
|
||||
ALTER NAMED COLLECTION collection2 DELETE key2
|
||||
@ -90,6 +116,13 @@ Change or add the key `key1` and delete the key `key3` of the collection `collec
|
||||
ALTER NAMED COLLECTION collection2 SET key1=4, DELETE key3
|
||||
```
|
||||
|
||||
To force a key to use the default settings for the `overridable` flag, you have to
|
||||
remove and re-add the key.
|
||||
```sql
|
||||
ALTER NAMED COLLECTION collection2 DELETE key1;
|
||||
ALTER NAMED COLLECTION collection2 SET key1=4;
|
||||
```
|
||||
|
||||
### Drop the DDL named collection `collection2`:
|
||||
```sql
|
||||
DROP NAMED COLLECTION collection2
|
||||
|
@ -12,9 +12,9 @@ This query intends to modify already existing named collections.
|
||||
```sql
|
||||
ALTER NAMED COLLECTION [IF EXISTS] name [ON CLUSTER cluster]
|
||||
[ SET
|
||||
key_name1 = 'some value',
|
||||
key_name2 = 'some value',
|
||||
key_name3 = 'some value',
|
||||
key_name1 = 'some value' [[NOT] OVERRIDABLE],
|
||||
key_name2 = 'some value' [[NOT] OVERRIDABLE],
|
||||
key_name3 = 'some value' [[NOT] OVERRIDABLE],
|
||||
... ] |
|
||||
[ DELETE key_name4, key_name5, ... ]
|
||||
```
|
||||
@ -22,9 +22,9 @@ key_name3 = 'some value',
|
||||
**Example**
|
||||
|
||||
```sql
|
||||
CREATE NAMED COLLECTION foobar AS a = '1', b = '2';
|
||||
CREATE NAMED COLLECTION foobar AS a = '1' NOT OVERRIDABLE, b = '2';
|
||||
|
||||
ALTER NAMED COLLECTION foobar SET a = '2', c = '3';
|
||||
ALTER NAMED COLLECTION foobar SET a = '2' OVERRIDABLE, c = '3';
|
||||
|
||||
ALTER NAMED COLLECTION foobar DELETE b;
|
||||
```
|
||||
|
@ -11,16 +11,16 @@ Creates a new named collection.
|
||||
|
||||
```sql
|
||||
CREATE NAMED COLLECTION [IF NOT EXISTS] name [ON CLUSTER cluster] AS
|
||||
key_name1 = 'some value',
|
||||
key_name2 = 'some value',
|
||||
key_name3 = 'some value',
|
||||
key_name1 = 'some value' [[NOT] OVERRIDABLE],
|
||||
key_name2 = 'some value' [[NOT] OVERRIDABLE],
|
||||
key_name3 = 'some value' [[NOT] OVERRIDABLE],
|
||||
...
|
||||
```
|
||||
|
||||
**Example**
|
||||
|
||||
```sql
|
||||
CREATE NAMED COLLECTION foobar AS a = '1', b = '2';
|
||||
CREATE NAMED COLLECTION foobar AS a = '1', b = '2' OVERRIDABLE;
|
||||
```
|
||||
|
||||
**Related statements**
|
||||
|
@ -123,6 +123,9 @@ template <typename T> void copyConfigValue(
|
||||
ErrorCodes::NOT_IMPLEMENTED,
|
||||
"Unsupported type in copyConfigValue(). "
|
||||
"Supported types are String, UInt64, Int64, Float64");
|
||||
const auto overridable = getOverridable(from_config, from_path);
|
||||
if (overridable)
|
||||
setOverridable(to_config, to_path, *overridable);
|
||||
}
|
||||
|
||||
void removeConfigValue(
|
||||
@ -147,13 +150,16 @@ ConfigurationPtr createEmptyConfiguration(const std::string & root_name)
|
||||
return config;
|
||||
}
|
||||
|
||||
ConfigurationPtr createConfiguration(const std::string & root_name, const SettingsChanges & settings)
|
||||
ConfigurationPtr
|
||||
createConfiguration(const std::string & root_name, const SettingsChanges & settings, const SettingsChanges & overridability)
|
||||
{
|
||||
namespace Configuration = NamedCollectionConfiguration;
|
||||
|
||||
auto config = Configuration::createEmptyConfiguration(root_name);
|
||||
for (const auto & [name, value] : settings)
|
||||
Configuration::setConfigValue<String>(*config, name, convertFieldToString(value));
|
||||
for (const auto & [name, value] : overridability)
|
||||
Configuration::setOverridable(*config, name, value.get<bool>());
|
||||
|
||||
return config;
|
||||
}
|
||||
@ -204,6 +210,20 @@ void listKeys(
|
||||
listKeys(config, enumerate_paths, result, depth);
|
||||
}
|
||||
|
||||
std::optional<bool> getOverridable(const Poco::Util::AbstractConfiguration & config, const std::string & path)
|
||||
{
|
||||
std::string overridable_path = path + "[@overridable]";
|
||||
if (config.has(overridable_path))
|
||||
return config.getBool(overridable_path);
|
||||
return {};
|
||||
}
|
||||
|
||||
void setOverridable(Poco::Util::AbstractConfiguration & config, const std::string & path, const bool value)
|
||||
{
|
||||
std::string overridable_path = path + "[@overridable]";
|
||||
config.setBool(overridable_path, value);
|
||||
}
|
||||
|
||||
template String getConfigValue<String>(const Poco::Util::AbstractConfiguration & config,
|
||||
const std::string & path);
|
||||
template UInt64 getConfigValue<UInt64>(const Poco::Util::AbstractConfiguration & config,
|
||||
|
@ -43,7 +43,8 @@ void removeConfigValue(
|
||||
Poco::Util::AbstractConfiguration & config,
|
||||
const std::string & path);
|
||||
|
||||
ConfigurationPtr createConfiguration(const std::string & root_name, const SettingsChanges & settings);
|
||||
ConfigurationPtr
|
||||
createConfiguration(const std::string & root_name, const SettingsChanges & settings, const SettingsChanges & overridability);
|
||||
|
||||
/// Enumerate keys paths of the config recursively.
|
||||
/// E.g. if `enumerate_paths` = {"root.key1"} and config like
|
||||
@ -67,6 +68,10 @@ void listKeys(
|
||||
std::queue<std::string> enumerate_paths,
|
||||
std::set<std::string, std::less<>> & result,
|
||||
ssize_t depth);
|
||||
|
||||
std::optional<bool> getOverridable(const Poco::Util::AbstractConfiguration & config, const std::string & path);
|
||||
|
||||
void setOverridable(Poco::Util::AbstractConfiguration & config, const std::string & path, bool value);
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -199,6 +199,12 @@ public:
|
||||
for (const auto & [name, value] : create_query.changes)
|
||||
result_changes_map.emplace(name, value);
|
||||
|
||||
std::unordered_map<std::string, Field> result_overridability_map;
|
||||
for (const auto & [name, value] : query.overridability)
|
||||
result_overridability_map.emplace(name, value);
|
||||
for (const auto & [name, value] : create_query.overridability)
|
||||
result_overridability_map.emplace(name, value);
|
||||
|
||||
for (const auto & delete_key : query.delete_keys)
|
||||
{
|
||||
auto it = result_changes_map.find(delete_key);
|
||||
@ -210,12 +216,20 @@ public:
|
||||
delete_key);
|
||||
}
|
||||
else
|
||||
{
|
||||
result_changes_map.erase(it);
|
||||
auto it_override = result_overridability_map.find(delete_key);
|
||||
if (it_override != result_overridability_map.end())
|
||||
result_overridability_map.erase(it_override);
|
||||
}
|
||||
}
|
||||
|
||||
create_query.changes.clear();
|
||||
for (const auto & [name, value] : result_changes_map)
|
||||
create_query.changes.emplace_back(name, value);
|
||||
create_query.overridability.clear();
|
||||
for (const auto & [name, value] : result_overridability_map)
|
||||
create_query.overridability.emplace_back(name, value);
|
||||
|
||||
writeCreateQueryToMetadata(
|
||||
create_query,
|
||||
@ -244,8 +258,7 @@ private:
|
||||
const ASTCreateNamedCollectionQuery & query)
|
||||
{
|
||||
const auto & collection_name = query.collection_name;
|
||||
const auto config = NamedCollectionConfiguration::createConfiguration(
|
||||
collection_name, query.changes);
|
||||
const auto config = NamedCollectionConfiguration::createConfiguration(collection_name, query.changes, query.overridability);
|
||||
|
||||
std::set<std::string, std::less<>> keys;
|
||||
for (const auto & [name, _] : query.changes)
|
||||
@ -448,6 +461,9 @@ void updateFromSQL(const ASTAlterNamedCollectionQuery & query, ContextPtr contex
|
||||
for (const auto & [name, value] : query.changes)
|
||||
collection->setOrUpdate<String, true>(name, convertFieldToString(value));
|
||||
|
||||
for (const auto & [name, value] : query.overridability)
|
||||
collection->setOverridable<true>(name, value.get<bool>());
|
||||
|
||||
for (const auto & key : query.delete_keys)
|
||||
collection->remove<true>(key);
|
||||
}
|
||||
|
@ -223,6 +223,16 @@ public:
|
||||
keys.insert(key);
|
||||
}
|
||||
|
||||
bool getOverridable(const Key & key, const bool default_value)
|
||||
{
|
||||
const auto overridable = Configuration::getOverridable(*config, key);
|
||||
if (overridable)
|
||||
return *overridable;
|
||||
return default_value;
|
||||
}
|
||||
|
||||
void setOverridable(const Key & key, const bool value) { Configuration::setOverridable(*config, key, value); }
|
||||
|
||||
ImplPtr createCopy(const std::string & collection_name_) const
|
||||
{
|
||||
return create(*config, collection_name_, "", keys);
|
||||
@ -414,6 +424,22 @@ template <typename T, bool Locked> void NamedCollection::setOrUpdate(const Key &
|
||||
pimpl->set<T>(key, value, true);
|
||||
}
|
||||
|
||||
bool NamedCollection::getOverridable(const Key & key, bool default_value) const
|
||||
{
|
||||
std::lock_guard lock(mutex);
|
||||
return pimpl->getOverridable(key, default_value);
|
||||
}
|
||||
|
||||
template <bool Locked>
|
||||
void NamedCollection::setOverridable(const Key & key, bool value)
|
||||
{
|
||||
assertMutable();
|
||||
std::unique_lock lock(mutex, std::defer_lock);
|
||||
if constexpr (!Locked)
|
||||
lock.lock();
|
||||
return pimpl->setOverridable(key, value);
|
||||
}
|
||||
|
||||
template <bool Locked> void NamedCollection::remove(const Key & key)
|
||||
{
|
||||
assertMutable();
|
||||
@ -519,6 +545,9 @@ template void NamedCollection::setOrUpdate<Float64, true>(const NamedCollection:
|
||||
template void NamedCollection::setOrUpdate<Float64, false>(const NamedCollection::Key & key, const Float64 & value);
|
||||
template void NamedCollection::setOrUpdate<bool, false>(const NamedCollection::Key & key, const bool & value);
|
||||
|
||||
template void NamedCollection::setOverridable<false>(const NamedCollection::Key & key, const bool value);
|
||||
template void NamedCollection::setOverridable<true>(const NamedCollection::Key & key, const bool value);
|
||||
|
||||
template void NamedCollection::remove<true>(const Key & key);
|
||||
template void NamedCollection::remove<false>(const Key & key);
|
||||
|
||||
|
@ -51,6 +51,11 @@ public:
|
||||
|
||||
template <typename T, bool locked = false> void setOrUpdate(const Key & key, const T & value);
|
||||
|
||||
bool getOverridable(const Key & key, bool default_value) const;
|
||||
|
||||
template <bool locked = false>
|
||||
void setOverridable(const Key & key, bool value);
|
||||
|
||||
template <bool locked = false> void remove(const Key & key);
|
||||
|
||||
MutableNamedCollectionPtr duplicate() const;
|
||||
|
@ -817,6 +817,7 @@ class IColumn;
|
||||
M(Bool, create_index_ignore_unique, false, "Ignore UNIQUE keyword in CREATE UNIQUE INDEX. Made for SQL compatibility tests.", 0) \
|
||||
M(Bool, print_pretty_type_names, false, "Print pretty type names in DESCRIBE query and toTypeName() function", 0) \
|
||||
M(Bool, create_table_empty_primary_key_by_default, false, "Allow to create *MergeTree tables with empty primary key when ORDER BY and PRIMARY KEY not specified", 0) \
|
||||
M(Bool, allow_named_collection_override_by_default, true, "Allow named collections' fields override by default.", 0)\
|
||||
|
||||
// End of COMMON_SETTINGS
|
||||
// Please add settings related to formats into the FORMAT_FACTORY_SETTINGS, move obsolete settings to OBSOLETE_SETTINGS and obsolete format settings to OBSOLETE_FORMAT_SETTINGS.
|
||||
|
@ -35,6 +35,9 @@ void ASTAlterNamedCollectionQuery::formatImpl(const IAST::FormatSettings & setti
|
||||
settings.ostr << " = " << applyVisitor(FieldVisitorToString(), change.value);
|
||||
else
|
||||
settings.ostr << " = '[HIDDEN]'";
|
||||
Field override_value;
|
||||
if (overridability.tryGet(change.name, override_value))
|
||||
settings.ostr << " " << (override_value.get<bool>() ? "" : "NOT ") << "OVERRIDABLE";
|
||||
}
|
||||
}
|
||||
if (!delete_keys.empty())
|
||||
|
@ -15,6 +15,7 @@ public:
|
||||
SettingsChanges changes;
|
||||
std::vector<std::string> delete_keys;
|
||||
bool if_exists = false;
|
||||
SettingsChanges overridability;
|
||||
|
||||
String getID(char) const override { return "AlterNamedCollectionQuery"; }
|
||||
|
||||
|
@ -39,6 +39,9 @@ void ASTCreateNamedCollectionQuery::formatImpl(const IAST::FormatSettings & sett
|
||||
settings.ostr << " = " << applyVisitor(FieldVisitorToString(), change.value);
|
||||
else
|
||||
settings.ostr << " = '[HIDDEN]'";
|
||||
Field override_value;
|
||||
if (overridability.tryGet(change.name, override_value))
|
||||
settings.ostr << " " << (override_value.get<bool>() ? "" : "NOT ") << "OVERRIDABLE";
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -14,6 +14,7 @@ public:
|
||||
std::string collection_name;
|
||||
SettingsChanges changes;
|
||||
bool if_not_exists = false;
|
||||
SettingsChanges overridability;
|
||||
|
||||
String getID(char) const override { return "CreateNamedCollectionQuery"; }
|
||||
|
||||
|
@ -17,14 +17,16 @@ bool ParserAlterNamedCollectionQuery::parseImpl(IParser::Pos & pos, ASTPtr & nod
|
||||
ParserKeyword s_on("ON");
|
||||
ParserKeyword s_delete("DELETE");
|
||||
ParserIdentifier name_p;
|
||||
ParserSetQuery set_p;
|
||||
ParserKeyword s_set("SET");
|
||||
ParserKeyword s_overridable("OVERRIDABLE");
|
||||
ParserKeyword s_not_overridable("NOT OVERRIDABLE");
|
||||
ParserToken s_comma(TokenType::Comma);
|
||||
|
||||
String cluster_str;
|
||||
bool if_exists = false;
|
||||
|
||||
ASTPtr collection_name;
|
||||
ASTPtr set;
|
||||
|
||||
std::vector<std::string> delete_keys;
|
||||
|
||||
if (!s_alter.ignore(pos, expected))
|
||||
@ -45,20 +47,27 @@ bool ParserAlterNamedCollectionQuery::parseImpl(IParser::Pos & pos, ASTPtr & nod
|
||||
return false;
|
||||
}
|
||||
|
||||
bool parsed_delete = false;
|
||||
if (!set_p.parse(pos, set, expected))
|
||||
SettingsChanges changes;
|
||||
SettingsChanges overridability;
|
||||
if (s_set.ignore(pos, expected))
|
||||
{
|
||||
if (!s_delete.ignore(pos, expected))
|
||||
return false;
|
||||
while (true)
|
||||
{
|
||||
if (!changes.empty() && !s_comma.ignore(pos))
|
||||
break;
|
||||
|
||||
parsed_delete = true;
|
||||
}
|
||||
else if (s_delete.ignore(pos, expected))
|
||||
{
|
||||
parsed_delete = true;
|
||||
changes.push_back(SettingChange{});
|
||||
|
||||
if (!ParserSetQuery::parseNameValuePair(changes.back(), pos, expected))
|
||||
return false;
|
||||
if (s_not_overridable.ignore(pos, expected))
|
||||
overridability.push_back(SettingChange{changes.back().name, false});
|
||||
else if (s_overridable.ignore(pos, expected))
|
||||
overridability.push_back(SettingChange{changes.back().name, true});
|
||||
}
|
||||
}
|
||||
|
||||
if (parsed_delete)
|
||||
if (s_delete.ignore(pos, expected))
|
||||
{
|
||||
while (true)
|
||||
{
|
||||
@ -78,8 +87,8 @@ bool ParserAlterNamedCollectionQuery::parseImpl(IParser::Pos & pos, ASTPtr & nod
|
||||
query->collection_name = getIdentifierName(collection_name);
|
||||
query->if_exists = if_exists;
|
||||
query->cluster = std::move(cluster_str);
|
||||
if (set)
|
||||
query->changes = set->as<ASTSetQuery>()->changes;
|
||||
query->changes = changes;
|
||||
query->overridability = overridability;
|
||||
query->delete_keys = delete_keys;
|
||||
|
||||
node = query;
|
||||
|
@ -1517,6 +1517,8 @@ bool ParserCreateNamedCollectionQuery::parseImpl(Pos & pos, ASTPtr & node, Expec
|
||||
ParserKeyword s_if_not_exists("IF NOT EXISTS");
|
||||
ParserKeyword s_on("ON");
|
||||
ParserKeyword s_as("AS");
|
||||
ParserKeyword s_not_overridable("NOT OVERRIDABLE");
|
||||
ParserKeyword s_overridable("OVERRIDABLE");
|
||||
ParserIdentifier name_p;
|
||||
ParserToken s_comma(TokenType::Comma);
|
||||
|
||||
@ -1547,6 +1549,7 @@ bool ParserCreateNamedCollectionQuery::parseImpl(Pos & pos, ASTPtr & node, Expec
|
||||
return false;
|
||||
|
||||
SettingsChanges changes;
|
||||
SettingsChanges overridability;
|
||||
|
||||
while (true)
|
||||
{
|
||||
@ -1557,6 +1560,10 @@ bool ParserCreateNamedCollectionQuery::parseImpl(Pos & pos, ASTPtr & node, Expec
|
||||
|
||||
if (!ParserSetQuery::parseNameValuePair(changes.back(), pos, expected))
|
||||
return false;
|
||||
if (s_not_overridable.ignore(pos, expected))
|
||||
overridability.push_back(SettingChange{changes.back().name, false});
|
||||
else if (s_overridable.ignore(pos, expected))
|
||||
overridability.push_back(SettingChange{changes.back().name, true});
|
||||
}
|
||||
|
||||
auto query = std::make_shared<ASTCreateNamedCollectionQuery>();
|
||||
@ -1565,6 +1572,7 @@ bool ParserCreateNamedCollectionQuery::parseImpl(Pos & pos, ASTPtr & node, Expec
|
||||
query->if_not_exists = if_not_exists;
|
||||
query->changes = changes;
|
||||
query->cluster = std::move(cluster_str);
|
||||
query->overridability = overridability;
|
||||
|
||||
node = query;
|
||||
return true;
|
||||
|
@ -92,14 +92,20 @@ MutableNamedCollectionPtr tryGetNamedCollectionWithOverrides(
|
||||
if (asts.size() == 1)
|
||||
return collection_copy;
|
||||
|
||||
const auto allow_override_by_default = context->getSettings().allow_named_collection_override_by_default;
|
||||
|
||||
for (auto * it = std::next(asts.begin()); it != asts.end(); ++it)
|
||||
{
|
||||
auto value_override = getKeyValueFromAST(*it, /* fallback_to_ast_value */complex_args != nullptr, context);
|
||||
|
||||
if (!value_override && !(*it)->as<ASTFunction>())
|
||||
throw Exception(ErrorCodes::BAD_ARGUMENTS, "Expected key-value argument or function");
|
||||
if (!value_override)
|
||||
if (!value_override && allow_override_by_default)
|
||||
continue;
|
||||
else if (!value_override)
|
||||
throw Exception(ErrorCodes::BAD_ARGUMENTS, "Override not allowed");
|
||||
else if (!collection_copy->getOverridable(value_override->first, allow_override_by_default))
|
||||
throw Exception(ErrorCodes::BAD_ARGUMENTS, "Override not allowed for '{}'", value_override->first);
|
||||
|
||||
if (const ASTPtr * value = std::get_if<ASTPtr>(&value_override->second))
|
||||
{
|
||||
@ -108,7 +114,7 @@ MutableNamedCollectionPtr tryGetNamedCollectionWithOverrides(
|
||||
}
|
||||
|
||||
const auto & [key, value] = *value_override;
|
||||
collection_copy->setOrUpdate<String>(key, toString(std::get<Field>(value_override->second)));
|
||||
collection_copy->setOrUpdate<String>(key, toString(std::get<Field>(value)));
|
||||
}
|
||||
|
||||
return collection_copy;
|
||||
@ -128,8 +134,14 @@ MutableNamedCollectionPtr tryGetNamedCollectionWithOverrides(
|
||||
|
||||
Poco::Util::AbstractConfiguration::Keys keys;
|
||||
config.keys(config_prefix, keys);
|
||||
const auto allow_override_by_default = context->getSettings().allow_named_collection_override_by_default;
|
||||
for (const auto & key : keys)
|
||||
collection_copy->setOrUpdate<String>(key, config.getString(config_prefix + '.' + key));
|
||||
{
|
||||
if (collection_copy->getOverridable(key, allow_override_by_default))
|
||||
collection_copy->setOrUpdate<String>(key, config.getString(config_prefix + '.' + key));
|
||||
else
|
||||
throw Exception(ErrorCodes::BAD_ARGUMENTS, "Override not allowed for '{}'", key);
|
||||
}
|
||||
|
||||
return collection_copy;
|
||||
}
|
||||
|
@ -37,5 +37,13 @@
|
||||
<access_key_id>test</access_key_id>
|
||||
<secret_access_key>testtest</secret_access_key>
|
||||
</s3_conn_db>
|
||||
<url_override1>
|
||||
<url overridable="0">http://127.0.0.1:8123?query=select+1</url>
|
||||
<format overridable="1">RawBLOB</format>
|
||||
</url_override1>
|
||||
<url_override2>
|
||||
<url>http://127.0.0.1:8123?query=select+1</url>
|
||||
<format>RawBLOB</format>
|
||||
</url_override2>
|
||||
</named_collections>
|
||||
</clickhouse>
|
||||
|
@ -0,0 +1,32 @@
|
||||
allow_named_collection_override_by_default=1 u1
|
||||
1\n
|
||||
1\n
|
||||
1
|
||||
allow_named_collection_override_by_default=1 u2
|
||||
1\n
|
||||
1\n
|
||||
2\n
|
||||
1
|
||||
allow_named_collection_override_by_default=0 u1
|
||||
1\n
|
||||
1
|
||||
allow_named_collection_override_by_default=0 u2
|
||||
1\n
|
||||
Test ALTER
|
||||
2\n
|
||||
1\n
|
||||
Test XML collections
|
||||
allow_named_collection_override_by_default=1 url_override1
|
||||
1\n
|
||||
1\n
|
||||
1
|
||||
allow_named_collection_override_by_default=1 url_override2
|
||||
1\n
|
||||
1\n
|
||||
2\n
|
||||
1
|
||||
allow_named_collection_override_by_default=0 url_override1
|
||||
1\n
|
||||
1
|
||||
allow_named_collection_override_by_default=0 url_override2
|
||||
1\n
|
@ -0,0 +1,73 @@
|
||||
DROP NAMED COLLECTION IF EXISTS u1;
|
||||
DROP NAMED COLLECTION IF EXISTS u2;
|
||||
|
||||
CREATE NAMED COLLECTION u1 AS
|
||||
url = 'http://127.0.0.1:8123?query=select+1' NOT OVERRIDABLE,
|
||||
format = 'RawBLOB' OVERRIDABLE;
|
||||
|
||||
CREATE NAMED COLLECTION u2 AS
|
||||
url = 'http://127.0.0.1:8123?query=select+1',
|
||||
format = 'RawBLOB';
|
||||
|
||||
SET allow_named_collection_override_by_default=1;
|
||||
SELECT 'allow_named_collection_override_by_default=1 u1';
|
||||
SELECT * FROM url(u1);
|
||||
SELECT * FROM url(u1, headers('Accept'='text/csv; charset=utf-8'));
|
||||
SELECT * FROM url(u1, url='http://127.0.0.1:8123?query=select+2'); -- { serverError BAD_ARGUMENTS }
|
||||
SELECT * FROM url(u1, format='CSV');
|
||||
SELECT 'allow_named_collection_override_by_default=1 u2';
|
||||
SELECT * FROM url(u2);
|
||||
SELECT * FROM url(u2, headers('Accept'='text/csv; charset=utf-8'));
|
||||
SELECT * FROM url(u2, url='http://127.0.0.1:8123?query=select+2');
|
||||
SELECT * FROM url(u2, format='CSV');
|
||||
|
||||
SET allow_named_collection_override_by_default=0;
|
||||
SELECT 'allow_named_collection_override_by_default=0 u1';
|
||||
SELECT * FROM url(u1);
|
||||
SELECT * FROM url(u1, headers('Accept'='text/csv; charset=utf-8')); -- { serverError BAD_ARGUMENTS }
|
||||
SELECT * FROM url(u1, url='http://127.0.0.1:8123?query=select+2'); -- { serverError BAD_ARGUMENTS }
|
||||
SELECT * FROM url(u1, format='CSV');
|
||||
SELECT 'allow_named_collection_override_by_default=0 u2';
|
||||
SELECT * FROM url(u2);
|
||||
SELECT * FROM url(u2, headers('Accept'='text/csv; charset=utf-8')); -- { serverError BAD_ARGUMENTS }
|
||||
SELECT * FROM url(u2, url='http://127.0.0.1:8123?query=select+2'); -- { serverError BAD_ARGUMENTS }
|
||||
SELECT * FROM url(u2, format='CSV'); -- { serverError BAD_ARGUMENTS }
|
||||
|
||||
SELECT 'Test ALTER';
|
||||
|
||||
ALTER NAMED COLLECTION u1 SET
|
||||
url = 'http://127.0.0.1:8123?query=select+2' OVERRIDABLE,
|
||||
format = 'RawBLOB' NOT OVERRIDABLE;
|
||||
|
||||
SELECT * FROM url(u1);
|
||||
SELECT * FROM url(u1, url='http://127.0.0.1:8123?query=select+1');
|
||||
SELECT * FROM url(u1, format='CSV'); -- { serverError BAD_ARGUMENTS }
|
||||
|
||||
DROP NAMED COLLECTION IF EXISTS u1;
|
||||
DROP NAMED COLLECTION IF EXISTS u2;
|
||||
|
||||
SELECT 'Test XML collections';
|
||||
|
||||
SET allow_named_collection_override_by_default=1;
|
||||
SELECT 'allow_named_collection_override_by_default=1 url_override1';
|
||||
SELECT * FROM url(url_override1);
|
||||
SELECT * FROM url(url_override1, headers('Accept'='text/csv; charset=utf-8'));
|
||||
SELECT * FROM url(url_override1, url='http://127.0.0.1:8123?query=select+2'); -- { serverError BAD_ARGUMENTS }
|
||||
SELECT * FROM url(url_override1, format='CSV');
|
||||
SELECT 'allow_named_collection_override_by_default=1 url_override2';
|
||||
SELECT * FROM url(url_override2);
|
||||
SELECT * FROM url(url_override2, headers('Accept'='text/csv; charset=utf-8'));
|
||||
SELECT * FROM url(url_override2, url='http://127.0.0.1:8123?query=select+2');
|
||||
SELECT * FROM url(url_override2, format='CSV');
|
||||
|
||||
SET allow_named_collection_override_by_default=0;
|
||||
SELECT 'allow_named_collection_override_by_default=0 url_override1';
|
||||
SELECT * FROM url(url_override1);
|
||||
SELECT * FROM url(url_override1, headers('Accept'='text/csv; charset=utf-8')); -- { serverError BAD_ARGUMENTS }
|
||||
SELECT * FROM url(url_override1, url='http://127.0.0.1:8123?query=select+2'); -- { serverError BAD_ARGUMENTS }
|
||||
SELECT * FROM url(url_override1, format='CSV');
|
||||
SELECT 'allow_named_collection_override_by_default=0 url_override2';
|
||||
SELECT * FROM url(url_override2);
|
||||
SELECT * FROM url(url_override2, headers('Accept'='text/csv; charset=utf-8')); -- { serverError BAD_ARGUMENTS }
|
||||
SELECT * FROM url(url_override2, url='http://127.0.0.1:8123?query=select+2'); -- { serverError BAD_ARGUMENTS }
|
||||
SELECT * FROM url(url_override2, format='CSV'); -- { serverError BAD_ARGUMENTS }
|
Loading…
Reference in New Issue
Block a user