ClickHouse
167 строк · 5.5 Кб
1#include <Functions/IFunction.h>2#include <Functions/FunctionFactory.h>3#include <Functions/FunctionHelpers.h>4#include <Columns/ColumnString.h>5#include <Columns/ColumnConst.h>6#include <DataTypes/DataTypesNumber.h>7#include <Storages/IStorage.h>8#include <Interpreters/Cluster.h>9#include <Interpreters/Context.h>10#include <Interpreters/DatabaseCatalog.h>11#include <Storages/getStructureOfRemoteTable.h>12
13
14namespace DB15{
16namespace ErrorCodes17{
18extern const int NUMBER_OF_ARGUMENTS_DOESNT_MATCH;19extern const int ILLEGAL_TYPE_OF_ARGUMENT;20extern const int UNKNOWN_TABLE;21}
22
23namespace
24{
25
26/** Usage:
27* hasColumnInTable(['hostname'[, 'username'[, 'password']],] 'database', 'table', 'column')
28*/
29class FunctionHasColumnInTable : public IFunction, WithContext30{
31public:32static constexpr auto name = "hasColumnInTable";33static FunctionPtr create(ContextPtr context_)34{35return std::make_shared<FunctionHasColumnInTable>(context_->getGlobalContext());36}37
38explicit FunctionHasColumnInTable(ContextPtr global_context_) : WithContext(global_context_)39{40}41
42bool isVariadic() const override43{44return true;45}46size_t getNumberOfArguments() const override47{48return 0;49}50
51String getName() const override52{53return name;54}55
56DataTypePtr getReturnTypeImpl(const ColumnsWithTypeAndName & arguments) const override;57
58bool isDeterministic() const override { return false; }59
60bool isSuitableForShortCircuitArgumentsExecution(const DataTypesWithConstInfo & /*arguments*/) const override { return false; }61
62ColumnPtr executeImpl(const ColumnsWithTypeAndName & arguments, const DataTypePtr &, size_t input_rows_count) const override;63};64
65
66DataTypePtr FunctionHasColumnInTable::getReturnTypeImpl(const ColumnsWithTypeAndName & arguments) const67{
68if (arguments.size() < 3 || arguments.size() > 6)69throw Exception(ErrorCodes::NUMBER_OF_ARGUMENTS_DOESNT_MATCH, "Invalid number of arguments for function {}", getName());70
71static const std::string arg_pos_description[] = {"First", "Second", "Third", "Fourth", "Fifth", "Sixth"};72for (size_t i = 0; i < arguments.size(); ++i)73{74const ColumnWithTypeAndName & argument = arguments[i];75
76if (!checkColumnConst<ColumnString>(argument.column.get()))77{78throw Exception(ErrorCodes::ILLEGAL_TYPE_OF_ARGUMENT, "{} argument for function {} must be const String.",79arg_pos_description[i], getName());80}81}82
83return std::make_shared<DataTypeUInt8>();84}
85
86
87ColumnPtr FunctionHasColumnInTable::executeImpl(const ColumnsWithTypeAndName & arguments, const DataTypePtr &, size_t input_rows_count) const88{
89auto get_string_from_columns = [&](const ColumnWithTypeAndName & column) -> String90{91const ColumnConst * const_column = checkAndGetColumnConst<ColumnString>(column.column.get());92return const_column->getValue<String>();93};94
95size_t arg = 0;96String host_name;97String user_name;98String password;99
100if (arguments.size() > 3)101host_name = get_string_from_columns(arguments[arg++]);102
103if (arguments.size() > 4)104user_name = get_string_from_columns(arguments[arg++]);105
106if (arguments.size() > 5)107password = get_string_from_columns(arguments[arg++]);108
109String database_name = get_string_from_columns(arguments[arg++]);110String table_name = get_string_from_columns(arguments[arg++]);111String column_name = get_string_from_columns(arguments[arg++]);112
113if (table_name.empty())114throw Exception(ErrorCodes::UNKNOWN_TABLE, "Table name is empty");115
116bool has_column;117if (host_name.empty())118{119// FIXME this (probably) needs a non-constant access to query context,120// because it might initialized a storage. Ideally, the tables required121// by the query should be initialized at an earlier stage.122const StoragePtr & table = DatabaseCatalog::instance().getTable(123{database_name, table_name},124const_pointer_cast<Context>(getContext()));125auto table_metadata = table->getInMemoryMetadataPtr();126has_column = table_metadata->getColumns().hasPhysical(column_name);127}128else129{130std::vector<std::vector<String>> host_names = {{ host_name }};131
132bool treat_local_as_remote = false;133bool treat_local_port_as_remote = getContext()->getApplicationType() == Context::ApplicationType::LOCAL;134ClusterConnectionParameters params{135!user_name.empty() ? user_name : "default",136password,137getContext()->getTCPPort(),138treat_local_as_remote,139treat_local_port_as_remote,140/* secure= */ false,141/* priority= */ Priority{1},142/* cluster_name= */ "",143/* password= */ ""144};145auto cluster = std::make_shared<Cluster>(getContext()->getSettings(), host_names, params);146
147// FIXME this (probably) needs a non-constant access to query context,148// because it might initialized a storage. Ideally, the tables required149// by the query should be initialized at an earlier stage.150auto remote_columns = getStructureOfRemoteTable(*cluster,151{database_name, table_name},152const_pointer_cast<Context>(getContext()));153
154has_column = remote_columns.hasPhysical(column_name);155}156
157return DataTypeUInt8().createColumnConst(input_rows_count, Field{static_cast<UInt64>(has_column)});158}
159
160}
161
162REGISTER_FUNCTION(HasColumnInTable)163{
164factory.registerFunction<FunctionHasColumnInTable>();165}
166
167}
168