aboutsummaryrefslogtreecommitdiffstats
path: root/contrib/clickhouse/src/Core/QualifiedTableName.h
blob: bf05bd59caf7b9c7f012cf4db783c48b025c4101 (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
#pragma once

#include <string>
#include <tuple>
#include <optional>
#include <Common/Exception.h>
#include <Common/SipHash.h>
#include <Common/quoteString.h>
#include <fmt/format.h>

namespace DB
{

namespace ErrorCodes
{
extern const int SYNTAX_ERROR;
}

//TODO replace with StorageID
struct QualifiedTableName
{
    std::string database;
    std::string table;

    bool operator==(const QualifiedTableName & other) const
    {
        return database == other.database && table == other.table;
    }

    bool operator<(const QualifiedTableName & other) const
    {
        return std::forward_as_tuple(database, table) < std::forward_as_tuple(other.database, other.table);
    }

    UInt64 hash() const
    {
        SipHash hash_state;
        hash_state.update(database.data(), database.size());
        hash_state.update(table.data(), table.size());
        return hash_state.get64();
    }

    std::vector<std::string> getParts() const
    {
        if (database.empty())
            return {table};
        else
            return {database, table};
    }

    std::string getFullName() const
    {
        if (database.empty())
            return table;
        else
            return database + '.' + table;
    }

    /// NOTE: It's different from compound identifier parsing and does not support escaping and dots in name.
    /// Usually it's better to use ParserIdentifier instead,
    /// but we parse DDL dictionary name (and similar things) this way for historical reasons.
    static std::optional<QualifiedTableName> tryParseFromString(const String & maybe_qualified_name)
    {
        if (maybe_qualified_name.empty())
            return {};

        /// Do not allow dot at the beginning and at the end
        auto pos = maybe_qualified_name.find('.');
        if (pos == 0 || pos == (maybe_qualified_name.size() - 1))
            return {};

        QualifiedTableName name;
        if (pos == std::string::npos)
        {
            name.table = maybe_qualified_name;
        }
        else if (maybe_qualified_name.find('.', pos + 1) != std::string::npos)
        {
            /// Do not allow multiple dots
            return {};
        }
        else
        {
            name.database = maybe_qualified_name.substr(0, pos);
            name.table = maybe_qualified_name.substr(pos + 1);
        }

        return name;
    }

    static QualifiedTableName parseFromString(const String & maybe_qualified_name)
    {
        auto name = tryParseFromString(maybe_qualified_name);
        if (!name)
            throw Exception(ErrorCodes::SYNTAX_ERROR, "Invalid qualified name: {}", maybe_qualified_name);
        return *name;
    }
};

}

namespace std
{

template <> struct hash<DB::QualifiedTableName>
{
    using argument_type = DB::QualifiedTableName;
    using result_type = size_t;

    result_type operator()(const argument_type & qualified_table) const
    {
        return qualified_table.hash();
    }
};
}

namespace fmt
{
    template <>
    struct formatter<DB::QualifiedTableName>
    {
        static constexpr auto parse(format_parse_context & ctx)
        {
            return ctx.begin();
        }

        template <typename FormatContext>
        auto format(const DB::QualifiedTableName & name, FormatContext & ctx)
        {
            return fmt::format_to(ctx.out(), "{}.{}", DB::backQuoteIfNeed(name.database), DB::backQuoteIfNeed(name.table));
        }
    };
}