aboutsummaryrefslogtreecommitdiffstats
path: root/contrib/clickhouse/src/TableFunctions/TableFunctionViewIfPermitted.cpp
blob: d7944df1b28d79227e4b136fd37dc6d0f7d46c6d (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
#include <Interpreters/InterpreterSelectWithUnionQuery.h>
#include <Interpreters/InterpreterSelectQueryAnalyzer.h>
#include <Parsers/ASTFunction.h>
#include <Parsers/ASTLiteral.h>
#include <Parsers/ASTSelectWithUnionQuery.h>
#include <Storages/StorageNull.h>
#include <Storages/StorageView.h>
#include <Storages/checkAndGetLiteralArgument.h>
#include <TableFunctions/ITableFunction.h>
#include <TableFunctions/TableFunctionFactory.h>
#include <TableFunctions/TableFunctionViewIfPermitted.h>
#include <Interpreters/parseColumnsListForTableFunction.h>
#include <Interpreters/evaluateConstantExpression.h>
#include "registerTableFunctions.h"


namespace DB
{
namespace ErrorCodes
{
    extern const int NUMBER_OF_ARGUMENTS_DOESNT_MATCH;
    extern const int BAD_ARGUMENTS;
    extern const int ACCESS_DENIED;
}


const ASTSelectWithUnionQuery & TableFunctionViewIfPermitted::getSelectQuery() const
{
    return *create.select;
}

std::vector<size_t> TableFunctionViewIfPermitted::skipAnalysisForArguments(const QueryTreeNodePtr &, ContextPtr) const
{
    return {0};
}

void TableFunctionViewIfPermitted::parseArguments(const ASTPtr & ast_function, ContextPtr context)
{
    const auto * function = ast_function->as<ASTFunction>();
    if (!function || !function->arguments || (function->arguments->children.size() != 2))
        throw Exception(
            ErrorCodes::NUMBER_OF_ARGUMENTS_DOESNT_MATCH,
            "Table function '{}' requires two arguments: a SELECT query and a table function",
            getName());

    const auto & arguments = function->arguments->children;
    auto * select = arguments[0]->as<ASTSelectWithUnionQuery>();
    if (!select)
        throw Exception(ErrorCodes::BAD_ARGUMENTS, "Table function '{}' requires a SELECT query as its first argument", getName());
    create.set(create.select, select->clone());

    else_ast = arguments[1];
    if (!else_ast->as<ASTFunction>())
        throw Exception(ErrorCodes::BAD_ARGUMENTS, "Table function '{}' requires a table function as its second argument", getName());
    else_table_function = TableFunctionFactory::instance().get(else_ast, context);
}

ColumnsDescription TableFunctionViewIfPermitted::getActualTableStructure(ContextPtr context, bool is_insert_query) const
{
    return else_table_function->getActualTableStructure(context, is_insert_query);
}

StoragePtr TableFunctionViewIfPermitted::executeImpl(
    const ASTPtr & /* ast_function */, ContextPtr context, const std::string & table_name, ColumnsDescription /* cached_columns */, bool is_insert_query) const
{
    StoragePtr storage;
    auto columns = getActualTableStructure(context, is_insert_query);

    if (isPermitted(context, columns))
    {
        storage = std::make_shared<StorageView>(StorageID(getDatabaseName(), table_name), create, columns, "");
    }
    else
    {
        storage = else_table_function->execute(else_ast, context, table_name);
    }

    storage->startup();
    return storage;
}

bool TableFunctionViewIfPermitted::isPermitted(const ContextPtr & context, const ColumnsDescription & else_columns) const
{
    Block sample_block;

    try
    {
        if (context->getSettingsRef().allow_experimental_analyzer)
        {
            sample_block = InterpreterSelectQueryAnalyzer::getSampleBlock(create.children[0], context);
        }
        else
        {
            /// Will throw ACCESS_DENIED if the current user is not allowed to execute the SELECT query.
            sample_block = InterpreterSelectWithUnionQuery::getSampleBlock(create.children[0], context);
        }
    }
    catch (Exception & e)
    {
        if (e.code() == ErrorCodes::ACCESS_DENIED)
            return false;
        throw;
    }

    /// We check that columns match only if permitted (otherwise we could reveal the structure to an user who must not know it).
    ColumnsDescription columns{sample_block.getNamesAndTypesList()};
    if (columns != else_columns)
    {
        throw Exception(
            ErrorCodes::BAD_ARGUMENTS,
            "Table function '{}' requires a SELECT query with the result columns matching a table function after 'ELSE'. "
            "Currently the result columns of the SELECT query are {}, and the table function after 'ELSE' gives {}",
            getName(),
            columns.toString(),
            else_columns.toString());
    }

    return true;
}

void registerTableFunctionViewIfPermitted(TableFunctionFactory & factory)
{
    factory.registerFunction<TableFunctionViewIfPermitted>({.documentation = {}, .allow_readonly = true});
}

}