| 1 | #include <Poco/Util/AbstractConfiguration.h> |
| 2 | #include <Common/Macros.h> |
| 3 | #include <Common/Exception.h> |
| 4 | #include <IO/WriteHelpers.h> |
| 5 | |
| 6 | |
| 7 | namespace DB |
| 8 | { |
| 9 | |
| 10 | namespace ErrorCodes |
| 11 | { |
| 12 | extern const int SYNTAX_ERROR; |
| 13 | } |
| 14 | |
| 15 | Macros::Macros() {} |
| 16 | |
| 17 | Macros::Macros(const Poco::Util::AbstractConfiguration & config, const String & root_key) |
| 18 | { |
| 19 | Poco::Util::AbstractConfiguration::Keys keys; |
| 20 | config.keys(root_key, keys); |
| 21 | for (const String & key : keys) |
| 22 | { |
| 23 | macros[key] = config.getString(root_key + "." + key); |
| 24 | } |
| 25 | } |
| 26 | |
| 27 | String Macros::expand(const String & s, size_t level, const String & database_name, const String & table_name) const |
| 28 | { |
| 29 | if (s.find('{') == String::npos) |
| 30 | return s; |
| 31 | |
| 32 | if (level && s.size() > 65536) |
| 33 | throw Exception("Too long string while expanding macros" , ErrorCodes::SYNTAX_ERROR); |
| 34 | |
| 35 | if (level >= 10) |
| 36 | throw Exception("Too deep recursion while expanding macros: '" + s + "'" , ErrorCodes::SYNTAX_ERROR); |
| 37 | |
| 38 | String res; |
| 39 | size_t pos = 0; |
| 40 | while (true) |
| 41 | { |
| 42 | size_t begin = s.find('{', pos); |
| 43 | |
| 44 | if (begin == String::npos) |
| 45 | { |
| 46 | res.append(s, pos, String::npos); |
| 47 | break; |
| 48 | } |
| 49 | else |
| 50 | { |
| 51 | res.append(s, pos, begin - pos); |
| 52 | } |
| 53 | |
| 54 | ++begin; |
| 55 | size_t end = s.find('}', begin); |
| 56 | if (end == String::npos) |
| 57 | throw Exception("Unbalanced { and } in string with macros: '" + s + "'" , ErrorCodes::SYNTAX_ERROR); |
| 58 | |
| 59 | String macro_name = s.substr(begin, end - begin); |
| 60 | auto it = macros.find(macro_name); |
| 61 | |
| 62 | /// Prefer explicit macros over implicit. |
| 63 | if (it != macros.end()) |
| 64 | res += it->second; |
| 65 | else if (macro_name == "database" && !database_name.empty()) |
| 66 | res += database_name; |
| 67 | else if (macro_name == "table" && !table_name.empty()) |
| 68 | res += table_name; |
| 69 | else |
| 70 | throw Exception("No macro '" + macro_name + |
| 71 | "' in config while processing substitutions in '" + s + "' at " |
| 72 | + toString(begin), ErrorCodes::SYNTAX_ERROR); |
| 73 | |
| 74 | pos = end + 1; |
| 75 | } |
| 76 | |
| 77 | return expand(res, level + 1, database_name, table_name); |
| 78 | } |
| 79 | |
| 80 | String Macros::getValue(const String & key) const |
| 81 | { |
| 82 | if (auto it = macros.find(key); it != macros.end()) |
| 83 | return it->second; |
| 84 | throw Exception("No macro " + key + " in config" , ErrorCodes::SYNTAX_ERROR); |
| 85 | } |
| 86 | |
| 87 | String Macros::expand(const String & s, const String & database_name, const String & table_name) const |
| 88 | { |
| 89 | return expand(s, 0, database_name, table_name); |
| 90 | } |
| 91 | |
| 92 | Names Macros::expand(const Names & source_names, size_t level) const |
| 93 | { |
| 94 | Names result_names; |
| 95 | result_names.reserve(source_names.size()); |
| 96 | |
| 97 | for (const String & name : source_names) |
| 98 | result_names.push_back(expand(name, level)); |
| 99 | |
| 100 | return result_names; |
| 101 | } |
| 102 | } |
| 103 | |