From 18f670defa30afb2725a34e95c5928e8747144c5 Mon Sep 17 00:00:00 2001 From: Alexey Milovidov Date: Sat, 5 Jul 2014 00:30:06 +0400 Subject: [PATCH] dbms: JOINs: development [#METR-11370]. --- .../DB/Interpreters/ExpressionAnalyzer.h | 27 +++-- dbms/src/Interpreters/ExpressionAnalyzer.cpp | 113 +++++++++++------- .../Interpreters/InterpreterSelectQuery.cpp | 4 +- 3 files changed, 90 insertions(+), 54 deletions(-) diff --git a/dbms/include/DB/Interpreters/ExpressionAnalyzer.h b/dbms/include/DB/Interpreters/ExpressionAnalyzer.h index 6ba5c8cf948..61037406752 100644 --- a/dbms/include/DB/Interpreters/ExpressionAnalyzer.h +++ b/dbms/include/DB/Interpreters/ExpressionAnalyzer.h @@ -98,15 +98,12 @@ public: */ Sets getSetsWithSubqueries(); Joins getJoinsWithSubqueries(); + + const Tables & getExternalTables() { return external_tables; } /// Если ast - запрос SELECT, получает имена (алиасы) и типы столбцов из секции SELECT. Block getSelectSampleBlock(); - /// Все новые временные таблицы, полученные при выполнении подзапросов GLOBAL IN. - Tables external_tables; - std::unordered_map external_data; - size_t external_table_id = 1; - /// Создаем какие сможем Set из секции In для использования индекса по ним void makeSetsForIndex(); @@ -169,6 +166,12 @@ private: /// Нужно ли подготавливать к выполнению глобальные подзапросы при анализировании запроса. bool do_global; + /// Все новые временные таблицы, полученные при выполнении подзапросов GLOBAL IN/JOIN. + Tables external_tables; + std::unordered_map external_data; + size_t external_table_id = 1; + + void init(); static NamesAndTypesList::iterator findColumn(const String & name, NamesAndTypesList & cols); @@ -194,13 +197,19 @@ private: void normalizeTree(); void normalizeTreeImpl(ASTPtr & ast, MapOfASTs & finished_asts, SetOfASTs & current_asts, std::string current_alias); - /// Находит в запросе использования внешних таблиц. Заполняет external_tables. - void findExternalTables(ASTPtr & ast); - /// Превратить перечисление значений или подзапрос в ASTSet. node - функция in или notIn. void makeSet(ASTFunction * node, const Block & sample_block); - /// Запустить подзапрос в секции GLOBAL IN/JOIN, создать временную таблицу типа Memory и запомнить эту пару в переменной external_tables. + /// Находит глобальные подзапросы в секциях GLOBAL IN/JOIN. Заполняет external_tables, external_data. + void initGlobalSubqueriesAndExternalTables(); + void initGlobalSubqueries(ASTPtr & ast); + + /// Находит в запросе использования внешних таблиц (в виде идентификаторов таблиц). Заполняет external_tables. + void findExternalTables(ASTPtr & ast); + + /** Инициализировать InterpreterSelectQuery для подзапроса в секции GLOBAL IN/JOIN, + * создать временную таблицу типа Memory и запомнить это в словаре external_tables, external_data. + */ void addExternalStorage(ASTPtr & subquery_or_table_name); void getArrayJoinedColumns(); diff --git a/dbms/src/Interpreters/ExpressionAnalyzer.cpp b/dbms/src/Interpreters/ExpressionAnalyzer.cpp index 0216e78bbbd..8307eadcda4 100644 --- a/dbms/src/Interpreters/ExpressionAnalyzer.cpp +++ b/dbms/src/Interpreters/ExpressionAnalyzer.cpp @@ -44,12 +44,9 @@ void ExpressionAnalyzer::init() /// Создаёт словарь aliases: alias -> ASTPtr createAliasesDict(ast); - /// Common subexpression elimination. Rewrite rules. addExternalStorage. + /// Common subexpression elimination. Rewrite rules. normalizeTree(); - /// Создаёт словарь external_tables: name -> StoragePtr. - findExternalTables(ast); - /// array_join_alias_to_name, array_join_result_to_source. getArrayJoinedColumns(); @@ -58,6 +55,10 @@ void ExpressionAnalyzer::init() /// has_aggregation, aggregation_keys, aggregate_descriptions, aggregated_columns. analyzeAggregation(); + + /// external_tables, external_data. + /// Заменяет глобальные подзапросы на сгенерированные имена временных таблиц, которые будут отправлены на удалённые серверы. + initGlobalSubqueriesAndExternalTables(); } @@ -131,6 +132,56 @@ void ExpressionAnalyzer::analyzeAggregation() } +void ExpressionAnalyzer::initGlobalSubqueriesAndExternalTables() +{ + initGlobalSubqueries(ast); + + /// Создаёт словарь external_tables: name -> StoragePtr. + findExternalTables(ast); +} + + +void ExpressionAnalyzer::initGlobalSubqueries(ASTPtr & ast) +{ + /// Рекурсивные вызовы. Не опускаемся в подзапросы. + + for (auto & child : ast->children) + if (!typeid_cast(&*child)) + initGlobalSubqueries(child); + + /// Действия, выполняемые снизу вверх. + + if (ASTFunction * node = typeid_cast(&*ast)) + { + /// Для GLOBAL IN. + if (do_global && (node->name == "globalIn" || node->name == "globalNotIn")) + addExternalStorage(node->arguments->children.at(1)); + } + else if (ASTJoin * node = typeid_cast(&*ast)) + { + /// Для GLOBAL JOIN. + if (do_global && node->locality == ASTJoin::Global) + addExternalStorage(node->table); + } +} + + +void ExpressionAnalyzer::findExternalTables(ASTPtr & ast) +{ + /// Обход снизу. Намеренно опускаемся в подзапросы. + for (auto & child : ast->children) + findExternalTables(child); + + /// Если идентификатор типа таблица + StoragePtr external_storage; + + if (ASTIdentifier * node = typeid_cast(&*ast)) + if (node->kind == ASTIdentifier::Kind::Table) + if ((external_storage = context.tryGetExternalTable(node->name))) + external_tables[node->name] = external_storage; +} + + NamesAndTypesList::iterator ExpressionAnalyzer::findColumn(const String & name, NamesAndTypesList & cols) { return std::find_if(cols.begin(), cols.end(), @@ -145,17 +196,17 @@ void ExpressionAnalyzer::createAliasesDict(ASTPtr & ast, int ignore_levels) ASTSelectQuery * select = typeid_cast(&*ast); /// Обход снизу-вверх. Не опускаемся в подзапросы. - for (ASTs::iterator it = ast->children.begin(); it != ast->children.end(); ++it) + for (auto & child : ast->children) { int new_ignore_levels = std::max(0, ignore_levels - 1); /// Алиасы верхнего уровня в секции ARRAY JOIN имеют особый смысл, их добавлять не будем /// (пропустим сам expression list и его детей). - if (select && *it == select->array_join_expression_list) + if (select && child == select->array_join_expression_list) new_ignore_levels = 2; - if (!typeid_cast(&**it)) - createAliasesDict(*it, new_ignore_levels); + if (!typeid_cast(&*child)) + createAliasesDict(child, new_ignore_levels); } if (ignore_levels > 0) @@ -295,9 +346,9 @@ void ExpressionAnalyzer::normalizeTreeImpl(ASTPtr & ast, MapOfASTs & finished_as /// Рекурсивные вызовы. Не опускаемся в подзапросы. - for (ASTs::iterator it = ast->children.begin(); it != ast->children.end(); ++it) - if (!typeid_cast(&**it)) - normalizeTreeImpl(*it, finished_asts, current_asts, current_alias); + for (auto & child : ast->children) + if (!typeid_cast(&*child)) + normalizeTreeImpl(child, finished_asts, current_asts, current_alias); /// Если секция WHERE или HAVING состоит из одного алиаса, ссылку нужно заменить не только в children, но и в where_expression и having_expression. if (ASTSelectQuery * select = typeid_cast(&*ast)) @@ -333,21 +384,12 @@ void ExpressionAnalyzer::normalizeTreeImpl(ASTPtr & ast, MapOfASTs & finished_as { node->kind = ASTFunction::FUNCTION; } - - /// Для GLOBAL IN. - if (do_global && (node->name == "globalIn" || node->name == "globalNotIn")) - addExternalStorage(node->arguments->children.at(1)); } - - if (ASTJoin * node = typeid_cast(&*ast)) + else if (ASTJoin * node = typeid_cast(&*ast)) { /// может быть указано JOIN t, где t - таблица, что равносильно JOIN (SELECT * FROM t). if (ASTIdentifier * right = typeid_cast(&*node->table)) right->kind = ASTIdentifier::Table; - - /// Для GLOBAL JOIN. - if (do_global && node->locality == ASTJoin::Global) - addExternalStorage(node->table); } current_asts.erase(initial_ast); @@ -390,22 +432,6 @@ void ExpressionAnalyzer::makeSetsForIndexImpl(ASTPtr & node, const Block & sampl } -void ExpressionAnalyzer::findExternalTables(ASTPtr & ast) -{ - /// Обход снизу. Намеренно опускаемся в подзапросы. - for (ASTs::iterator it = ast->children.begin(); it != ast->children.end(); ++it) - findExternalTables(*it); - - /// Если идентификатор типа таблица - StoragePtr external_storage; - - if (ASTIdentifier * node = typeid_cast(&*ast)) - if (node->kind == ASTIdentifier::Kind::Table) - if ((external_storage = context.tryGetExternalTable(node->name))) - external_tables[node->name] = external_storage; -} - - void ExpressionAnalyzer::addExternalStorage(ASTPtr & subquery_or_table_name) { /// Сгенерируем имя для внешней таблицы. @@ -500,7 +526,7 @@ void ExpressionAnalyzer::makeSet(ASTFunction * node, const Block & sample_block) if (typeid_cast(&*arg)) return; - /// Если подзапрос или имя таблицы для селекта + /// Если подзапрос или имя таблицы для SELECT. if (typeid_cast(&*arg) || typeid_cast(&*arg)) { /// Получаем поток блоков для подзапроса, отдаем его множеству, и кладём это множество на место подзапроса. @@ -854,9 +880,9 @@ void ExpressionAnalyzer::getArrayJoinedColumnsImpl(ASTPtr ast) } else { - for (ASTs::iterator it = ast->children.begin(); it != ast->children.end(); ++it) - if (!typeid_cast(&**it)) - getArrayJoinedColumnsImpl(*it); + for (auto & child : ast->children) + if (!typeid_cast(&*child)) + getArrayJoinedColumnsImpl(child); } } @@ -917,6 +943,7 @@ void ExpressionAnalyzer::getActionsImpl(ASTPtr ast, bool no_subqueries, bool onl { /// Найдем тип первого аргумента (потом getActionsImpl вызовется для него снова и ни на что не повлияет). getActionsImpl(node->arguments->children[0], no_subqueries, only_consts, actions_stack); + /// Превратим tuple или подзапрос в множество. makeSet(node, actions_stack.getSampleBlock()); } @@ -1104,8 +1131,8 @@ void ExpressionAnalyzer::getActionsImpl(ASTPtr ast, bool no_subqueries, bool onl } else { - for (ASTs::iterator it = ast->children.begin(); it != ast->children.end(); ++it) - getActionsImpl(*it, no_subqueries, only_consts, actions_stack); + for (auto & child : ast->children) + getActionsImpl(child, no_subqueries, only_consts, actions_stack); } } diff --git a/dbms/src/Interpreters/InterpreterSelectQuery.cpp b/dbms/src/Interpreters/InterpreterSelectQuery.cpp index 05cba0cec96..e483db55b56 100644 --- a/dbms/src/Interpreters/InterpreterSelectQuery.cpp +++ b/dbms/src/Interpreters/InterpreterSelectQuery.cpp @@ -79,7 +79,7 @@ void InterpreterSelectQuery::init(BlockInputStreamPtr input_, const NamesAndType query_analyzer = new ExpressionAnalyzer(query_ptr, context, storage, subquery_depth, true); /// Сохраняем в query context новые временные таблицы - for (auto & it : query_analyzer->external_tables) + for (auto & it : query_analyzer->getExternalTables()) if (!(context.tryGetExternalTable(it.first))) context.addExternalTable(it.first, it.second); @@ -505,7 +505,7 @@ QueryProcessingStage::Enum InterpreterSelectQuery::executeFetchColumns(BlockInpu if (!interpreter_subquery) { if (storage->isRemote()) - storage->storeExternalTables(query_analyzer->external_tables); + storage->storeExternalTables(query_analyzer->getExternalTables()); streams = storage->read(required_columns, query_ptr, settings_for_storage, from_stage, settings.max_block_size, settings.max_threads); for (auto stream : streams) {