ClickHouse/src/Common/Macros.cpp

168 lines
5.4 KiB
C++
Raw Normal View History

2018-03-13 23:44:23 +00:00
#include <Poco/Util/AbstractConfiguration.h>
#include <Common/Macros.h>
#include <Common/Exception.h>
2019-11-05 13:05:48 +00:00
#include <IO/WriteHelpers.h>
2020-09-26 19:18:28 +00:00
#include <common/logger_useful.h>
2014-08-11 15:59:01 +00:00
2018-03-13 23:44:23 +00:00
2014-08-11 15:59:01 +00:00
namespace DB
{
namespace ErrorCodes
{
extern const int SYNTAX_ERROR;
}
2020-09-26 19:18:28 +00:00
Macros::Macros(const Poco::Util::AbstractConfiguration & config, const String & root_key, Poco::Logger * log)
2014-08-11 15:59:01 +00:00
{
Poco::Util::AbstractConfiguration::Keys keys;
config.keys(root_key, keys);
for (const String & key : keys)
{
macros[key] = config.getString(root_key + "." + key);
2020-09-26 19:18:28 +00:00
if (key == "database" || key == "table" || key == "uuid")
{
2020-09-27 11:10:45 +00:00
if (log)
LOG_WARNING(log,
"Config file contains '{}' macro. This macro has special meaning "
"and it's explicit definition is not recommended. Implicit unfolding for "
"'database', 'table' and 'uuid' macros will be disabled.",
key);
2020-09-26 19:18:28 +00:00
enable_special_macros = false;
}
}
2014-08-11 15:59:01 +00:00
}
2020-07-10 09:19:32 +00:00
String Macros::expand(const String & s,
MacroExpansionInfo & info) const
2014-08-11 15:59:01 +00:00
{
2020-09-26 19:18:28 +00:00
/// Do not allow recursion if we expand only special macros, because it will be infinite recursion
assert(info.level == 0 || !info.expand_special_macros_only);
if (s.find('{') == String::npos)
return s;
2014-08-11 15:59:01 +00:00
if (info.level && s.size() > 65536)
throw Exception("Too long string while expanding macros", ErrorCodes::SYNTAX_ERROR);
if (info.level >= 10)
throw Exception("Too deep recursion while expanding macros: '" + s + "'", ErrorCodes::SYNTAX_ERROR);
2020-09-26 19:18:28 +00:00
/// If config file contains explicit special macro, then we do not expand it in this mode.
if (!enable_special_macros && info.expand_special_macros_only)
return s;
String res;
size_t pos = 0;
while (true)
{
size_t begin = s.find('{', pos);
2014-08-11 15:59:01 +00:00
if (begin == String::npos)
{
res.append(s, pos, String::npos);
break;
}
else
{
res.append(s, pos, begin - pos);
}
2014-08-11 15:59:01 +00:00
++begin;
size_t end = s.find('}', begin);
if (end == String::npos)
throw Exception("Unbalanced { and } in string with macros: '" + s + "'", ErrorCodes::SYNTAX_ERROR);
2014-08-11 15:59:01 +00:00
String macro_name = s.substr(begin, end - begin);
2018-10-01 09:01:50 +00:00
auto it = macros.find(macro_name);
2014-08-11 15:59:01 +00:00
2018-10-01 09:01:50 +00:00
/// Prefer explicit macros over implicit.
2020-09-26 19:18:28 +00:00
if (it != macros.end() && !info.expand_special_macros_only)
2018-10-01 09:01:50 +00:00
res += it->second;
2020-09-26 19:18:28 +00:00
else if (macro_name == "database" && !info.table_id.database_name.empty())
{
res += info.table_id.database_name;
info.expanded_database = true;
}
else if (macro_name == "table" && !info.table_id.table_name.empty())
{
res += info.table_id.table_name;
info.expanded_table = true;
}
else if (macro_name == "uuid")
{
2020-09-26 19:18:28 +00:00
if (info.table_id.uuid == UUIDHelpers::Nil)
throw Exception("Macro 'uuid' and empty arguments of ReplicatedMergeTree "
"are supported only for ON CLUSTER queries with Atomic database engine",
ErrorCodes::SYNTAX_ERROR);
/// For ON CLUSTER queries we don't want to require all macros definitions in initiator's config.
/// However, initiator must check that for cross-replication cluster zookeeper_path does not contain {uuid} macro.
/// It becomes impossible to check if {uuid} is contained inside some unknown macro.
if (info.level)
throw Exception("Macro 'uuid' should not be inside another macro", ErrorCodes::SYNTAX_ERROR);
2020-09-26 19:18:28 +00:00
res += toString(info.table_id.uuid);
info.expanded_uuid = true;
}
2020-09-26 19:18:28 +00:00
else if (info.ignore_unknown || info.expand_special_macros_only)
{
2020-09-26 19:18:28 +00:00
if (info.expand_special_macros_only)
res += '{';
res += macro_name;
2020-09-26 19:18:28 +00:00
if (info.expand_special_macros_only)
res += '}';
info.has_unknown = true;
}
2018-09-27 10:01:10 +00:00
else
2019-11-05 13:05:48 +00:00
throw Exception("No macro '" + macro_name +
2020-07-10 09:19:32 +00:00
"' in config while processing substitutions in '" + s + "' at '"
+ toString(begin) + "' or macro is not supported here", ErrorCodes::SYNTAX_ERROR);
2014-08-11 15:59:01 +00:00
pos = end + 1;
}
2014-08-11 15:59:01 +00:00
++info.level;
2020-09-26 19:18:28 +00:00
if (info.expand_special_macros_only)
return res;
return expand(res, info);
2014-08-11 15:59:01 +00:00
}
2019-10-09 01:14:57 +00:00
String Macros::getValue(const String & key) const
{
if (auto it = macros.find(key); it != macros.end())
return it->second;
throw Exception("No macro " + key + " in config", ErrorCodes::SYNTAX_ERROR);
}
String Macros::expand(const String & s) const
{
MacroExpansionInfo info;
return expand(s, info);
}
2020-07-10 09:19:32 +00:00
String Macros::expand(const String & s, const StorageID & table_id, bool allow_uuid) const
2018-10-01 09:01:50 +00:00
{
MacroExpansionInfo info;
2020-09-26 19:18:28 +00:00
info.table_id = table_id;
if (!allow_uuid)
info.table_id.uuid = UUIDHelpers::Nil;
return expand(s, info);
2018-10-01 09:01:50 +00:00
}
2018-04-20 16:08:27 +00:00
Names Macros::expand(const Names & source_names, size_t level) const
{
2018-04-20 16:08:27 +00:00
Names result_names;
result_names.reserve(source_names.size());
MacroExpansionInfo info;
2018-04-20 16:08:27 +00:00
for (const String & name : source_names)
{
info.level = level;
result_names.push_back(expand(name, info));
}
2018-05-07 02:01:11 +00:00
2018-04-20 16:08:27 +00:00
return result_names;
}
2014-08-11 15:59:01 +00:00
}