aboutsummaryrefslogtreecommitdiffstats
path: root/contrib/clickhouse/src/Storages/NamedCollectionsHelpers.h
blob: 3d0ff5d8dabda280e57779745e489349d1b6a1e6 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
#pragma once
#include <Parsers/IAST_fwd.h>
#include <IO/HTTPHeaderEntries.h>
#include <Common/NamedCollections/NamedCollections.h>
#include <Common/quoteString.h>
#include <unordered_set>
#include <string_view>
#include <fmt/format.h>
#include <regex>

namespace ErrorCodes
{
    extern const int BAD_ARGUMENTS;
}

namespace DB
{

/// Helper function to get named collection for table engine.
/// Table engines have collection name as first argument of ast and other arguments are key-value overrides.
MutableNamedCollectionPtr tryGetNamedCollectionWithOverrides(
    ASTs asts, ContextPtr context, bool throw_unknown_collection = true, std::vector<std::pair<std::string, ASTPtr>> * complex_args = nullptr);
/// Helper function to get named collection for dictionary source.
/// Dictionaries have collection name as name argument of dict configuration and other arguments are overrides.
MutableNamedCollectionPtr tryGetNamedCollectionWithOverrides(const Poco::Util::AbstractConfiguration & config, const std::string & config_prefix, ContextPtr context);

HTTPHeaderEntries getHeadersFromNamedCollection(const NamedCollection & collection);

struct ExternalDatabaseEqualKeysSet
{
    static constexpr std::array<std::pair<std::string_view, std::string_view>, 5> equal_keys{
        std::pair{"username", "user"}, std::pair{"database", "db"}, std::pair{"hostname", "host"}, std::pair{"addresses_expr", "host"}, std::pair{"addresses_expr", "hostname"}};
};
struct MongoDBEqualKeysSet
{
    static constexpr std::array<std::pair<std::string_view, std::string_view>, 4> equal_keys{
        std::pair{"username", "user"}, std::pair{"database", "db"}, std::pair{"hostname", "host"}, std::pair{"table", "collection"}};
};
struct RedisEqualKeysSet
{
    static constexpr std::array<std::pair<std::string_view, std::string_view>, 4> equal_keys{std::pair{"hostname", "host"}};
};

template <typename EqualKeys> struct NamedCollectionValidateKey
{
    NamedCollectionValidateKey() = default;
    NamedCollectionValidateKey(const char * value_) : value(value_) {}
    NamedCollectionValidateKey(std::string_view value_) : value(value_) {}
    NamedCollectionValidateKey(const String & value_) : value(value_) {}

    std::string_view value;

    bool operator==(const auto & other) const
    {
        if (value == other.value)
            return true;

        for (const auto & equal : EqualKeys::equal_keys)
        {
            if (((equal.first == value) && (equal.second == other.value)) || ((equal.first == other.value) && (equal.second == value)))
            {
                return true;
            }
        }
        return false;
    }

    bool operator<(const auto & other) const
    {
        std::string_view canonical_self = value;
        std::string_view canonical_other = other.value;
        for (const auto & equal : EqualKeys::equal_keys)
        {
            if ((equal.first == value) || (equal.second == value))
                canonical_self = std::max(canonical_self, std::max(equal.first, equal.second));
            if ((equal.first == other.value) || (equal.second == other.value))
                canonical_other = std::max(canonical_other, std::max(equal.first, equal.second));
        }

        return canonical_self < canonical_other;
    }
};

template <typename T>
std::ostream & operator << (std::ostream & ostr, const NamedCollectionValidateKey<T> & key)
{
    ostr << key.value;
    return ostr;
}

template <class keys_cmp> using ValidateKeysMultiset = std::multiset<NamedCollectionValidateKey<keys_cmp>, std::less<NamedCollectionValidateKey<keys_cmp>>>;
using ValidateKeysSet = std::multiset<std::string_view>;

template <typename Keys = ValidateKeysSet>
void validateNamedCollection(
    const NamedCollection & collection,
    const Keys & required_keys,
    const Keys & optional_keys,
    const std::vector<std::regex> & optional_regex_keys = {})
{
    NamedCollection::Keys keys = collection.getKeys();
    auto required_keys_copy = required_keys;

    for (const auto & key : keys)
    {
        if (required_keys_copy.contains(key))
        {
            required_keys_copy.erase(key);
            continue;
        }

        if (optional_keys.contains(key))
        {
            continue;
        }

        if (required_keys.contains(key))
            throw Exception(ErrorCodes::BAD_ARGUMENTS, "Duplicate key {} in named collection", key);

        auto match = std::find_if(
            optional_regex_keys.begin(), optional_regex_keys.end(),
            [&](const std::regex & regex) { return std::regex_search(key, regex); })
            != optional_regex_keys.end();

        if (!match)
        {
             throw Exception(
                 ErrorCodes::BAD_ARGUMENTS,
                 "Unexpected key {} in named collection. Required keys: {}, optional keys: {}",
                 backQuoteIfNeed(key), fmt::join(required_keys, ", "), fmt::join(optional_keys, ", "));
        }
    }

    if (!required_keys_copy.empty())
    {
        throw Exception(
            ErrorCodes::BAD_ARGUMENTS,
            "Required keys ({}) are not specified. All required keys: {}, optional keys: {}",
            fmt::join(required_keys_copy, ", "), fmt::join(required_keys, ", "), fmt::join(optional_keys, ", "));
    }
}

}

template <typename T>
struct fmt::formatter<DB::NamedCollectionValidateKey<T>>
{
    constexpr static auto parse(format_parse_context & context)
    {
        return context.begin();
    }

    template <typename FormatContext>
    auto format(const DB::NamedCollectionValidateKey<T> & elem, FormatContext & context)
    {
        return fmt::format_to(context.out(), "{}", elem.value);
    }
};