2017-04-01 09:19:00 +00:00
|
|
|
#include <Common/OptimizedRegularExpression.h>
|
2017-07-13 20:58:19 +00:00
|
|
|
#include <Common/typeid_cast.h>
|
2017-04-01 09:19:00 +00:00
|
|
|
#include <Storages/StorageMerge.h>
|
|
|
|
#include <Parsers/ASTLiteral.h>
|
|
|
|
#include <Parsers/ASTFunction.h>
|
|
|
|
#include <TableFunctions/ITableFunction.h>
|
|
|
|
#include <Interpreters/evaluateConstantExpression.h>
|
|
|
|
#include <Interpreters/Context.h>
|
2020-12-10 20:16:53 +00:00
|
|
|
#include <Access/ContextAccess.h>
|
2017-04-01 09:19:00 +00:00
|
|
|
#include <TableFunctions/TableFunctionMerge.h>
|
2017-06-10 09:04:31 +00:00
|
|
|
#include <TableFunctions/TableFunctionFactory.h>
|
2020-02-19 18:58:29 +00:00
|
|
|
#include <TableFunctions/registerTableFunctions.h>
|
2016-05-13 03:22:16 +00:00
|
|
|
|
|
|
|
|
|
|
|
namespace DB
|
|
|
|
{
|
|
|
|
|
|
|
|
namespace ErrorCodes
|
|
|
|
{
|
2017-04-01 07:20:54 +00:00
|
|
|
extern const int NUMBER_OF_ARGUMENTS_DOESNT_MATCH;
|
|
|
|
extern const int UNKNOWN_TABLE;
|
2016-05-13 03:22:16 +00:00
|
|
|
}
|
|
|
|
|
2020-12-10 20:16:53 +00:00
|
|
|
namespace
|
2020-12-15 15:07:20 +00:00
|
|
|
{
|
2021-06-07 09:14:29 +00:00
|
|
|
[[noreturn]] void throwNoTablesMatchRegexp(const String & source_database_regexp, const String & source_table_regexp)
|
2020-12-15 15:07:20 +00:00
|
|
|
{
|
2020-12-10 20:16:53 +00:00
|
|
|
throw Exception(
|
2021-06-07 09:14:29 +00:00
|
|
|
"Error while executing table function merge. Neither no one database matches regular expression " + source_database_regexp
|
|
|
|
+ " nor in database matches " + source_database_regexp + " no one table matches regular expression: " + source_table_regexp,
|
2020-12-10 20:16:53 +00:00
|
|
|
ErrorCodes::UNKNOWN_TABLE);
|
2020-12-15 15:07:20 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-12-10 20:16:53 +00:00
|
|
|
|
2021-04-10 23:33:54 +00:00
|
|
|
void TableFunctionMerge::parseArguments(const ASTPtr & ast_function, ContextPtr context)
|
2016-05-13 03:22:16 +00:00
|
|
|
{
|
2019-03-11 12:49:39 +00:00
|
|
|
ASTs & args_func = ast_function->children;
|
2016-05-13 03:22:16 +00:00
|
|
|
|
2017-04-01 07:20:54 +00:00
|
|
|
if (args_func.size() != 1)
|
2017-06-10 09:04:31 +00:00
|
|
|
throw Exception("Table function 'merge' requires exactly 2 arguments"
|
2017-04-01 07:20:54 +00:00
|
|
|
" - name of source database and regexp for table names.",
|
|
|
|
ErrorCodes::NUMBER_OF_ARGUMENTS_DOESNT_MATCH);
|
2016-05-13 03:22:16 +00:00
|
|
|
|
2019-03-11 12:49:39 +00:00
|
|
|
ASTs & args = args_func.at(0)->children;
|
2016-05-13 03:22:16 +00:00
|
|
|
|
2017-04-01 07:20:54 +00:00
|
|
|
if (args.size() != 2)
|
2017-06-10 09:04:31 +00:00
|
|
|
throw Exception("Table function 'merge' requires exactly 2 arguments"
|
2017-04-01 07:20:54 +00:00
|
|
|
" - name of source database and regexp for table names.",
|
|
|
|
ErrorCodes::NUMBER_OF_ARGUMENTS_DOESNT_MATCH);
|
2016-05-13 03:22:16 +00:00
|
|
|
|
2021-06-27 06:09:23 +00:00
|
|
|
auto [is_regexp, database_ast] = evaluateDatabaseNameForMergeEngine(args[0], context);
|
2021-06-25 13:51:17 +00:00
|
|
|
|
|
|
|
database_is_regexp = is_regexp;
|
2021-06-27 06:09:23 +00:00
|
|
|
|
|
|
|
if (!is_regexp)
|
|
|
|
args[0] = database_ast;
|
|
|
|
source_database_name_or_regexp = database_ast->as<ASTLiteral &>().value.safeGet<String>();
|
2016-08-25 17:23:29 +00:00
|
|
|
|
2021-06-25 13:51:17 +00:00
|
|
|
args[1] = evaluateConstantExpressionAsLiteral(args[1], context);
|
2021-06-13 08:03:19 +00:00
|
|
|
source_table_regexp = args[1]->as<ASTLiteral &>().value.safeGet<String>();
|
2020-10-14 12:19:29 +00:00
|
|
|
}
|
2020-08-26 20:56:30 +00:00
|
|
|
|
2020-12-10 20:16:53 +00:00
|
|
|
|
2021-06-04 14:48:48 +00:00
|
|
|
const std::unordered_map<String, std::unordered_set<String>> & TableFunctionMerge::getSourceDatabasesAndTables(ContextPtr context) const
|
2020-12-10 20:16:53 +00:00
|
|
|
{
|
2021-06-04 14:48:48 +00:00
|
|
|
if (source_databases_and_tables)
|
|
|
|
return *source_databases_and_tables;
|
2020-12-10 20:16:53 +00:00
|
|
|
|
2021-06-25 13:51:17 +00:00
|
|
|
source_databases_and_tables.emplace();
|
2020-12-10 20:16:53 +00:00
|
|
|
|
2021-06-25 13:51:17 +00:00
|
|
|
/// database_name is not a regexp
|
|
|
|
if (!database_is_regexp)
|
|
|
|
{
|
|
|
|
auto source_tables = getMatchedTablesWithAccess(source_database_name_or_regexp, source_table_regexp, context);
|
|
|
|
if (source_tables.empty())
|
|
|
|
throwNoTablesMatchRegexp(source_database_name_or_regexp, source_table_regexp);
|
|
|
|
(*source_databases_and_tables)[source_database_name_or_regexp] = source_tables;
|
|
|
|
}
|
2021-06-07 09:14:29 +00:00
|
|
|
|
2021-06-25 13:51:17 +00:00
|
|
|
/// database_name is a regexp
|
|
|
|
else
|
2020-12-10 20:16:53 +00:00
|
|
|
{
|
2021-06-25 13:51:17 +00:00
|
|
|
OptimizedRegularExpression database_re(source_database_name_or_regexp);
|
|
|
|
auto databases = DatabaseCatalog::instance().getDatabases();
|
|
|
|
|
|
|
|
for (const auto & db : databases)
|
2021-06-07 09:14:29 +00:00
|
|
|
{
|
2021-06-25 13:51:17 +00:00
|
|
|
if (database_re.match(db.first))
|
2021-06-07 09:14:29 +00:00
|
|
|
{
|
2021-06-25 13:51:17 +00:00
|
|
|
auto source_tables = getMatchedTablesWithAccess(db.first, source_table_regexp, context);
|
2021-06-07 09:14:29 +00:00
|
|
|
|
2021-06-25 13:51:17 +00:00
|
|
|
if (!source_tables.empty())
|
|
|
|
(*source_databases_and_tables)[db.first] = source_tables;
|
|
|
|
}
|
2021-06-07 09:14:29 +00:00
|
|
|
}
|
2020-12-10 20:16:53 +00:00
|
|
|
|
2021-06-25 13:51:17 +00:00
|
|
|
if ((*source_databases_and_tables).empty())
|
|
|
|
throwNoTablesMatchRegexp(source_database_name_or_regexp, source_table_regexp);
|
|
|
|
}
|
2020-12-10 20:16:53 +00:00
|
|
|
|
2021-06-07 09:14:29 +00:00
|
|
|
return *source_databases_and_tables;
|
2020-12-10 20:16:53 +00:00
|
|
|
}
|
|
|
|
|
2021-04-10 23:33:54 +00:00
|
|
|
ColumnsDescription TableFunctionMerge::getActualTableStructure(ContextPtr context) const
|
2020-10-14 12:19:29 +00:00
|
|
|
{
|
2021-06-07 09:14:29 +00:00
|
|
|
for (const auto & db_with_tables : getSourceDatabasesAndTables(context))
|
2020-12-10 20:16:53 +00:00
|
|
|
{
|
2021-06-07 09:14:29 +00:00
|
|
|
auto storage = DatabaseCatalog::instance().tryGetTable(StorageID{db_with_tables.first, *db_with_tables.second.begin()}, context);
|
2020-12-10 20:16:53 +00:00
|
|
|
if (storage)
|
|
|
|
return ColumnsDescription{storage->getInMemoryMetadataPtr()->getColumns().getAllPhysical()};
|
|
|
|
}
|
|
|
|
|
2021-06-25 13:51:17 +00:00
|
|
|
throwNoTablesMatchRegexp(source_database_name_or_regexp, source_table_regexp);
|
2020-10-14 12:19:29 +00:00
|
|
|
}
|
|
|
|
|
2020-12-10 20:16:53 +00:00
|
|
|
|
2021-04-10 23:33:54 +00:00
|
|
|
StoragePtr TableFunctionMerge::executeImpl(const ASTPtr & /*ast_function*/, ContextPtr context, const std::string & table_name, ColumnsDescription /*cached_columns*/) const
|
2020-10-14 12:19:29 +00:00
|
|
|
{
|
2020-08-31 17:45:21 +00:00
|
|
|
auto res = StorageMerge::create(
|
2019-12-04 16:06:55 +00:00
|
|
|
StorageID(getDatabaseName(), table_name),
|
2020-10-14 12:19:29 +00:00
|
|
|
getActualTableStructure(context),
|
2021-04-23 12:18:23 +00:00
|
|
|
String{},
|
2021-06-25 13:51:17 +00:00
|
|
|
source_database_name_or_regexp,
|
|
|
|
database_is_regexp,
|
2021-06-04 14:48:48 +00:00
|
|
|
getSourceDatabasesAndTables(context),
|
2017-04-01 07:20:54 +00:00
|
|
|
context);
|
2020-10-14 12:19:29 +00:00
|
|
|
|
2017-06-06 17:06:14 +00:00
|
|
|
res->startup();
|
|
|
|
return res;
|
2016-05-13 03:22:16 +00:00
|
|
|
}
|
|
|
|
|
2021-06-25 13:51:17 +00:00
|
|
|
NameSet
|
|
|
|
TableFunctionMerge::getMatchedTablesWithAccess(const String & database_name, const String & table_regexp, const ContextPtr & context)
|
|
|
|
{
|
|
|
|
OptimizedRegularExpression table_re(table_regexp);
|
|
|
|
|
|
|
|
auto table_name_match = [&](const String & table_name) { return table_re.match(table_name); };
|
|
|
|
|
|
|
|
auto access = context->getAccess();
|
|
|
|
|
|
|
|
auto database = DatabaseCatalog::instance().getDatabase(database_name);
|
|
|
|
|
|
|
|
bool granted_show_on_all_tables = access->isGranted(AccessType::SHOW_TABLES, database_name);
|
|
|
|
bool granted_select_on_all_tables = access->isGranted(AccessType::SELECT, database_name);
|
|
|
|
|
|
|
|
NameSet tables;
|
|
|
|
|
|
|
|
for (auto it = database->getTablesIterator(context, table_name_match); it->isValid(); it->next())
|
|
|
|
{
|
|
|
|
if (!it->table())
|
|
|
|
continue;
|
|
|
|
bool granted_show = granted_show_on_all_tables || access->isGranted(AccessType::SHOW_TABLES, database_name, it->name());
|
|
|
|
if (!granted_show)
|
|
|
|
continue;
|
|
|
|
if (!granted_select_on_all_tables)
|
|
|
|
access->checkAccess(AccessType::SELECT, database_name, it->name());
|
|
|
|
tables.insert(it->name());
|
|
|
|
}
|
|
|
|
return tables;
|
|
|
|
}
|
2017-06-10 09:04:31 +00:00
|
|
|
|
|
|
|
void registerTableFunctionMerge(TableFunctionFactory & factory)
|
|
|
|
{
|
2017-12-02 02:47:12 +00:00
|
|
|
factory.registerFunction<TableFunctionMerge>();
|
2017-06-10 09:04:31 +00:00
|
|
|
}
|
|
|
|
|
2016-05-13 03:22:16 +00:00
|
|
|
}
|