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 | |