diff --git a/dbms/include/DB/Interpreters/ExpressionAnalyzer.h b/dbms/include/DB/Interpreters/ExpressionAnalyzer.h index d7111b1345f..d7a1350b4f5 100644 --- a/dbms/include/DB/Interpreters/ExpressionAnalyzer.h +++ b/dbms/include/DB/Interpreters/ExpressionAnalyzer.h @@ -182,6 +182,9 @@ private: /// Например, для ARRAY JOIN [1,2] AS b сюда попадет "b" -> "array(1,2)". NameToNameMap array_join_alias_to_name; + /// Обратное отображение для array_join_alias_to_name. + NameToNameMap array_join_name_to_alias; + /// Нужно ли подготавливать к выполнению глобальные подзапросы при анализировании запроса. bool do_global; diff --git a/dbms/src/Interpreters/ExpressionActions.cpp b/dbms/src/Interpreters/ExpressionActions.cpp index 86f2647a01f..3d59cca3a2e 100644 --- a/dbms/src/Interpreters/ExpressionActions.cpp +++ b/dbms/src/Interpreters/ExpressionActions.cpp @@ -148,9 +148,9 @@ void ExpressionAction::prepare(Block & sample_block) case ARRAY_JOIN: { - for (NameSet::iterator it = array_joined_columns.begin(); it != array_joined_columns.end(); ++it) + for (const auto & name : array_joined_columns) { - ColumnWithTypeAndName & current = sample_block.getByName(*it); + ColumnWithTypeAndName & current = sample_block.getByName(name); const DataTypeArray * array_type = typeid_cast(&*current.type); if (!array_type) throw Exception("ARRAY JOIN requires array argument", ErrorCodes::TYPE_MISMATCH); @@ -214,6 +214,7 @@ void ExpressionAction::prepare(Block & sample_block) } } + void ExpressionAction::execute(Block & block) const { // std::cerr << "executing: " << toString() << std::endl; @@ -261,9 +262,11 @@ void ExpressionAction::execute(Block & block) const { if (array_joined_columns.empty()) throw Exception("No arrays to join", ErrorCodes::LOGICAL_ERROR); + ColumnPtr any_array_ptr = block.getByName(*array_joined_columns.begin()).column; if (any_array_ptr->isConst()) any_array_ptr = dynamic_cast(*any_array_ptr).convertToFullColumn(); + const ColumnArray * any_array = typeid_cast(&*any_array_ptr); if (!any_array) throw Exception("ARRAY JOIN of not array: " + *array_joined_columns.begin(), ErrorCodes::TYPE_MISMATCH); diff --git a/dbms/src/Interpreters/ExpressionAnalyzer.cpp b/dbms/src/Interpreters/ExpressionAnalyzer.cpp index 3c347d62a28..71b4fe794b5 100644 --- a/dbms/src/Interpreters/ExpressionAnalyzer.cpp +++ b/dbms/src/Interpreters/ExpressionAnalyzer.cpp @@ -1407,12 +1407,15 @@ void ExpressionAnalyzer::getArrayJoinedColumns() { const String nested_table_name = ast->getColumnName(); const String nested_table_alias = ast->getAliasOrColumnName(); + if (nested_table_alias == nested_table_name && !typeid_cast(&*ast)) throw Exception("No alias for non-trivial value in ARRAY JOIN: " + nested_table_name, ErrorCodes::ALIAS_REQUIRED); if (array_join_alias_to_name.count(nested_table_alias) || aliases.count(nested_table_alias)) throw Exception("Duplicate alias " + nested_table_alias, ErrorCodes::MULTIPLE_EXPRESSIONS_FOR_ALIAS); + array_join_alias_to_name[nested_table_alias] = nested_table_name; + array_join_name_to_alias[nested_table_name] = nested_table_alias; } ASTs & query_asts = select_query->children; @@ -1455,6 +1458,7 @@ void ExpressionAnalyzer::getArrayJoinedColumns() } +/// Заполняет array_join_result_to_source: по каким столбцам-массивам размножить, и как их после этого назвать. void ExpressionAnalyzer::getArrayJoinedColumnsImpl(ASTPtr ast) { if (ASTIdentifier * node = typeid_cast(&*ast)) @@ -1462,14 +1466,29 @@ void ExpressionAnalyzer::getArrayJoinedColumnsImpl(ASTPtr ast) if (node->kind == ASTIdentifier::Column) { String table_name = DataTypeNested::extractNestedTableName(node->name); + if (array_join_alias_to_name.count(node->name)) - array_join_result_to_source[node->name] = array_join_alias_to_name[node->name]; + { + /// Был написан ARRAY JOIN со столбцом-массивом. Пример: SELECT K1 FROM ... ARRAY JOIN ParsedParams.Key1 AS K1 + array_join_result_to_source[node->name] = array_join_alias_to_name[node->name]; /// K1 -> ParsedParams.Key1 + } else if (array_join_alias_to_name.count(table_name)) { - String nested_column = DataTypeNested::extractNestedColumnName(node->name); - array_join_result_to_source[node->name] + /// Был написан ARRAY JOIN с вложенной таблицей. Пример: SELECT PP.Key1 FROM ... ARRAY JOIN ParsedParams AS PP + String nested_column = DataTypeNested::extractNestedColumnName(node->name); /// Key1 + array_join_result_to_source[node->name] /// PP.Key1 -> ParsedParams.Key1 = DataTypeNested::concatenateNestedName(array_join_alias_to_name[table_name], nested_column); } + else if (array_join_name_to_alias.count(table_name)) + { + /** Пример: SELECT ParsedParams.Key1 FROM ... ARRAY JOIN ParsedParams AS PP. + * То есть, в запросе используется исходный массив, размноженный по самому себе. + */ + + String nested_column = DataTypeNested::extractNestedColumnName(node->name); /// Key1 + array_join_result_to_source[ /// PP.Key1 -> ParsedParams.Key1 + DataTypeNested::concatenateNestedName(array_join_name_to_alias[table_name], nested_column)] = node->name; + } } } else @@ -1511,10 +1530,12 @@ void ExpressionAnalyzer::getActionsImpl(ASTPtr ast, bool no_subqueries, bool onl if (node->kind == ASTFunction::LAMBDA_EXPRESSION) throw Exception("Unexpected expression", ErrorCodes::UNEXPECTED_EXPRESSION); + /// Функция arrayJoin. if (node->kind == ASTFunction::ARRAY_JOIN) { if (node->arguments->children.size() != 1) throw Exception("arrayJoin requires exactly 1 argument", ErrorCodes::TYPE_MISMATCH); + ASTPtr arg = node->arguments->children.at(0); getActionsImpl(arg, no_subqueries, only_consts, actions_stack); if (!only_consts) @@ -1810,13 +1831,17 @@ void ExpressionAnalyzer::initChain(ExpressionActionsChain & chain, const NamesAn } } +/// "Большой" ARRAY JOIN. void ExpressionAnalyzer::addMultipleArrayJoinAction(ExpressionActionsPtr & actions) const { NameSet result_columns; for (const auto & result_source : array_join_result_to_source) { + /// Дать столбцам новые имена, если надо. if (result_source.first != result_source.second) actions->add(ExpressionAction::copyColumn(result_source.second, result_source.first)); + + /// Сделать ARRAY JOIN (заменить массивы на их внутренности) для столбцов в этими новыми именами. result_columns.insert(result_source.first); }