aboutsummaryrefslogtreecommitdiffstats
path: root/contrib/clickhouse/src/Interpreters/InterpreterShowIndexesQuery.cpp
blob: 149420006fb7e4066bc1a3c88bea154deb98d79c (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
#include <Interpreters/InterpreterShowIndexesQuery.h>

#include <Common/quoteString.h>
#include <IO/Operators.h>
#include <IO/WriteBufferFromString.h>
#include <Parsers/ASTShowIndexesQuery.h>
#include <Parsers/formatAST.h>
#include <Interpreters/Context.h>
#include <Interpreters/executeQuery.h>


namespace DB
{


InterpreterShowIndexesQuery::InterpreterShowIndexesQuery(const ASTPtr & query_ptr_, ContextMutablePtr context_)
    : WithMutableContext(context_)
    , query_ptr(query_ptr_)
{
}


String InterpreterShowIndexesQuery::getRewrittenQuery()
{
    const auto & query = query_ptr->as<ASTShowIndexesQuery &>();

    WriteBufferFromOwnString buf_table;
    writeEscapedString(query.table, buf_table);
    String table = buf_table.str();

    WriteBufferFromOwnString buf_database;
    String resolved_database = getContext()->resolveDatabase(query.database);
    writeEscapedString(resolved_database, buf_database);
    String database = buf_database.str();

    String where_expression = query.where_expression ? fmt::format("WHERE ({})", query.where_expression) : "";

    String rewritten_query = fmt::format(R"(
SELECT *
FROM (
        (SELECT
            name AS table,
            1 AS non_unique,
            'PRIMARY' AS key_name,
            row_number() over (order by column_name) AS seq_in_index,
            arrayJoin(splitByString(', ', primary_key)) AS column_name,
            'A' AS collation,
            0 AS cardinality,
            NULL AS sub_part,
            NULL AS packed,
            NULL AS null,
            'PRIMARY' AS index_type,
            '' AS comment,
            '' AS index_comment,
            'YES' AS visible,
            '' AS expression
        FROM system.tables
        WHERE
            database = '{0}'
            AND name = '{1}')
    UNION ALL (
        SELECT
            table AS table,
            1 AS non_unique,
            name AS key_name,
            1 AS seq_in_index,
            '' AS column_name,
            NULL AS collation,
            0 AS cardinality,
            NULL AS sub_part,
            NULL AS packed,
            NULL AS null,
            upper(type) AS index_type,
            '' AS comment,
            '' AS index_comment,
            'YES' AS visible,
            expr AS expression
        FROM system.data_skipping_indices
        WHERE
            database = '{0}'
            AND table = '{1}'))
{2}
ORDER BY index_type, expression, column_name, seq_in_index;)", database, table, where_expression);

    /// Sorting is strictly speaking not necessary but 1. it is convenient for users, 2. SQL currently does not allow to
    /// sort the output of SHOW INDEXES otherwise (SELECT * FROM (SHOW INDEXES ...) ORDER BY ...) is rejected) and 3. some
    /// SQL tests can take advantage of this.

    /// Note about compatibility of fields 'column_name', 'seq_in_index' and 'expression' with MySQL:
    /// MySQL has non-functional and functional indexes.
    /// - Non-functional indexes only reference columns, e.g. 'col1, col2'. In this case, `SHOW INDEX` produces as many result rows as there
    ///   are indexed columns. 'column_name' and 'seq_in_index' (an ascending integer 1, 2, ...) are filled, 'expression' is empty.
    /// - Functional indexes can reference arbitrary expressions, e.g. 'col1 + 1, concat(col2, col3)'. 'SHOW INDEX' produces a single row
    ///   with `column_name` and `seq_in_index` empty and `expression` filled with the entire index expression. Only non-primary-key indexes
    ///   can be functional indexes.
    /// Above SELECT tries to emulate that. Caveats:
    /// 1. The primary key index sub-SELECT assumes the primary key expression is non-functional. Non-functional primary key indexes in
    ///    ClickHouse are possible but quiete obscure. In MySQL they are not possible at all.
    /// 2. Related to 1.: Poor man's tuple parsing with splitByString() in the PK sub-SELECT messes up for functional primary key index
    ///    expressions where the comma is not only used as separator between tuple components, e.g. in 'col1 + 1, concat(col2, col3)'.
    /// 3. The data skipping index sub-SELECT assumes the index expression is functional. 3rd party tools that expect MySQL semantics from
    ///    SHOW INDEX will probably not care as MySQL has no skipping indexes and they only use the result to figure out the primary key.

    return rewritten_query;
}


BlockIO InterpreterShowIndexesQuery::execute()
{
    return executeQuery(getRewrittenQuery(), getContext(), true);
}


}