Merge pull request #63993 from ClickHouse/experiment-do-not-add-identifiers-to-expr-map

Check what would be broken if do not add all the identifiers to functions map.
This commit is contained in:
Nikolai Kochetov 2024-05-24 09:29:40 +00:00 committed by GitHub
commit 523634b1c7
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
4 changed files with 173 additions and 144 deletions

View File

@ -586,6 +586,85 @@ private:
std::unordered_map<std::string, QueryTreeNodes> alias_name_to_expressions; std::unordered_map<std::string, QueryTreeNodes> alias_name_to_expressions;
}; };
struct ScopeAliases
{
/// Alias name to query expression node
std::unordered_map<std::string, QueryTreeNodePtr> alias_name_to_expression_node_before_group_by;
std::unordered_map<std::string, QueryTreeNodePtr> alias_name_to_expression_node_after_group_by;
std::unordered_map<std::string, QueryTreeNodePtr> * alias_name_to_expression_node = nullptr;
/// Alias name to lambda node
std::unordered_map<std::string, QueryTreeNodePtr> alias_name_to_lambda_node;
/// Alias name to table expression node
std::unordered_map<std::string, QueryTreeNodePtr> alias_name_to_table_expression_node;
/// Expressions like `x as y` where we can't say whether it's a function, expression or table.
std::unordered_map<std::string, Identifier> transitive_aliases;
/// Nodes with duplicated aliases
std::unordered_set<QueryTreeNodePtr> nodes_with_duplicated_aliases;
std::vector<QueryTreeNodePtr> cloned_nodes_with_duplicated_aliases;
std::unordered_map<std::string, QueryTreeNodePtr> & getAliasMap(IdentifierLookupContext lookup_context)
{
switch (lookup_context)
{
case IdentifierLookupContext::EXPRESSION: return *alias_name_to_expression_node;
case IdentifierLookupContext::FUNCTION: return alias_name_to_lambda_node;
case IdentifierLookupContext::TABLE_EXPRESSION: return alias_name_to_table_expression_node;
}
}
enum class FindOption
{
FIRST_NAME,
FULL_NAME,
};
const std::string & getKey(const Identifier & identifier, FindOption find_option)
{
switch (find_option)
{
case FindOption::FIRST_NAME: return identifier.front();
case FindOption::FULL_NAME: return identifier.getFullName();
}
}
QueryTreeNodePtr * find(IdentifierLookup lookup, FindOption find_option)
{
auto & alias_map = getAliasMap(lookup.lookup_context);
const std::string * key = &getKey(lookup.identifier, find_option);
auto it = alias_map.find(*key);
if (it != alias_map.end())
return &it->second;
if (lookup.lookup_context == IdentifierLookupContext::TABLE_EXPRESSION)
return {};
while (it == alias_map.end())
{
auto jt = transitive_aliases.find(*key);
if (jt == transitive_aliases.end())
return {};
key = &(getKey(jt->second, find_option));
it = alias_map.find(*key);
}
return &it->second;
}
const QueryTreeNodePtr * find(IdentifierLookup lookup, FindOption find_option) const
{
return const_cast<ScopeAliases *>(this)->find(lookup, find_option);
}
};
/** Projection names is name of query tree node that is used in projection part of query node. /** Projection names is name of query tree node that is used in projection part of query node.
* Example: SELECT id FROM test_table; * Example: SELECT id FROM test_table;
* `id` is projection name of column node * `id` is projection name of column node
@ -731,7 +810,7 @@ struct IdentifierResolveScope
else if (parent_scope) else if (parent_scope)
join_use_nulls = parent_scope->join_use_nulls; join_use_nulls = parent_scope->join_use_nulls;
alias_name_to_expression_node = &alias_name_to_expression_node_before_group_by; aliases.alias_name_to_expression_node = &aliases.alias_name_to_expression_node_before_group_by;
} }
QueryTreeNodePtr scope_node; QueryTreeNodePtr scope_node;
@ -746,17 +825,7 @@ struct IdentifierResolveScope
/// Argument can be expression like constant, column, function or table expression /// Argument can be expression like constant, column, function or table expression
std::unordered_map<std::string, QueryTreeNodePtr> expression_argument_name_to_node; std::unordered_map<std::string, QueryTreeNodePtr> expression_argument_name_to_node;
/// Alias name to query expression node ScopeAliases aliases;
std::unordered_map<std::string, QueryTreeNodePtr> alias_name_to_expression_node_before_group_by;
std::unordered_map<std::string, QueryTreeNodePtr> alias_name_to_expression_node_after_group_by;
std::unordered_map<std::string, QueryTreeNodePtr> * alias_name_to_expression_node = nullptr;
/// Alias name to lambda node
std::unordered_map<std::string, QueryTreeNodePtr> alias_name_to_lambda_node;
/// Alias name to table expression node
std::unordered_map<std::string, QueryTreeNodePtr> alias_name_to_table_expression_node;
/// Table column name to column node. Valid only during table ALIAS columns resolve. /// Table column name to column node. Valid only during table ALIAS columns resolve.
ColumnNameToColumnNodeMap column_name_to_column_node; ColumnNameToColumnNodeMap column_name_to_column_node;
@ -767,10 +836,6 @@ struct IdentifierResolveScope
/// Window name to window node /// Window name to window node
std::unordered_map<std::string, QueryTreeNodePtr> window_name_to_window_node; std::unordered_map<std::string, QueryTreeNodePtr> window_name_to_window_node;
/// Nodes with duplicated aliases
std::unordered_set<QueryTreeNodePtr> nodes_with_duplicated_aliases;
std::vector<QueryTreeNodePtr> cloned_nodes_with_duplicated_aliases;
/// Current scope expression in resolve process stack /// Current scope expression in resolve process stack
ExpressionsStack expressions_in_resolve_process_stack; ExpressionsStack expressions_in_resolve_process_stack;
@ -889,7 +954,7 @@ struct IdentifierResolveScope
bool had_aggregate_function = expressions_in_resolve_process_stack.hasAggregateFunction(); bool had_aggregate_function = expressions_in_resolve_process_stack.hasAggregateFunction();
expressions_in_resolve_process_stack.push(node); expressions_in_resolve_process_stack.push(node);
if (group_by_use_nulls && had_aggregate_function != expressions_in_resolve_process_stack.hasAggregateFunction()) if (group_by_use_nulls && had_aggregate_function != expressions_in_resolve_process_stack.hasAggregateFunction())
alias_name_to_expression_node = &alias_name_to_expression_node_before_group_by; aliases.alias_name_to_expression_node = &aliases.alias_name_to_expression_node_before_group_by;
} }
void popExpressionNode() void popExpressionNode()
@ -897,7 +962,7 @@ struct IdentifierResolveScope
bool had_aggregate_function = expressions_in_resolve_process_stack.hasAggregateFunction(); bool had_aggregate_function = expressions_in_resolve_process_stack.hasAggregateFunction();
expressions_in_resolve_process_stack.pop(); expressions_in_resolve_process_stack.pop();
if (group_by_use_nulls && had_aggregate_function != expressions_in_resolve_process_stack.hasAggregateFunction()) if (group_by_use_nulls && had_aggregate_function != expressions_in_resolve_process_stack.hasAggregateFunction())
alias_name_to_expression_node = &alias_name_to_expression_node_after_group_by; aliases.alias_name_to_expression_node = &aliases.alias_name_to_expression_node_after_group_by;
} }
/// Dump identifier resolve scope /// Dump identifier resolve scope
@ -916,16 +981,16 @@ struct IdentifierResolveScope
for (const auto & [alias_name, node] : expression_argument_name_to_node) for (const auto & [alias_name, node] : expression_argument_name_to_node)
buffer << "Alias name " << alias_name << " node " << node->formatASTForErrorMessage() << '\n'; buffer << "Alias name " << alias_name << " node " << node->formatASTForErrorMessage() << '\n';
buffer << "Alias name to expression node table size " << alias_name_to_expression_node->size() << '\n'; buffer << "Alias name to expression node table size " << aliases.alias_name_to_expression_node->size() << '\n';
for (const auto & [alias_name, node] : *alias_name_to_expression_node) for (const auto & [alias_name, node] : *aliases.alias_name_to_expression_node)
buffer << "Alias name " << alias_name << " expression node " << node->dumpTree() << '\n'; buffer << "Alias name " << alias_name << " expression node " << node->dumpTree() << '\n';
buffer << "Alias name to function node table size " << alias_name_to_lambda_node.size() << '\n'; buffer << "Alias name to function node table size " << aliases.alias_name_to_lambda_node.size() << '\n';
for (const auto & [alias_name, node] : alias_name_to_lambda_node) for (const auto & [alias_name, node] : aliases.alias_name_to_lambda_node)
buffer << "Alias name " << alias_name << " lambda node " << node->formatASTForErrorMessage() << '\n'; buffer << "Alias name " << alias_name << " lambda node " << node->formatASTForErrorMessage() << '\n';
buffer << "Alias name to table expression node table size " << alias_name_to_table_expression_node.size() << '\n'; buffer << "Alias name to table expression node table size " << aliases.alias_name_to_table_expression_node.size() << '\n';
for (const auto & [alias_name, node] : alias_name_to_table_expression_node) for (const auto & [alias_name, node] : aliases.alias_name_to_table_expression_node)
buffer << "Alias name " << alias_name << " node " << node->formatASTForErrorMessage() << '\n'; buffer << "Alias name " << alias_name << " node " << node->formatASTForErrorMessage() << '\n';
buffer << "CTE name to query node table size " << cte_name_to_query_node.size() << '\n'; buffer << "CTE name to query node table size " << cte_name_to_query_node.size() << '\n';
@ -936,8 +1001,8 @@ struct IdentifierResolveScope
for (const auto & [window_name, node] : window_name_to_window_node) for (const auto & [window_name, node] : window_name_to_window_node)
buffer << "CTE name " << window_name << " node " << node->formatASTForErrorMessage() << '\n'; buffer << "CTE name " << window_name << " node " << node->formatASTForErrorMessage() << '\n';
buffer << "Nodes with duplicated aliases size " << nodes_with_duplicated_aliases.size() << '\n'; buffer << "Nodes with duplicated aliases size " << aliases.nodes_with_duplicated_aliases.size() << '\n';
for (const auto & node : nodes_with_duplicated_aliases) for (const auto & node : aliases.nodes_with_duplicated_aliases)
buffer << "Alias name " << node->getAlias() << " node " << node->formatASTForErrorMessage() << '\n'; buffer << "Alias name " << node->getAlias() << " node " << node->formatASTForErrorMessage() << '\n';
buffer << "Expression resolve process stack " << '\n'; buffer << "Expression resolve process stack " << '\n';
@ -996,8 +1061,8 @@ struct IdentifierResolveScope
class QueryExpressionsAliasVisitor : public InDepthQueryTreeVisitor<QueryExpressionsAliasVisitor> class QueryExpressionsAliasVisitor : public InDepthQueryTreeVisitor<QueryExpressionsAliasVisitor>
{ {
public: public:
explicit QueryExpressionsAliasVisitor(IdentifierResolveScope & scope_) explicit QueryExpressionsAliasVisitor(ScopeAliases & aliases_)
: scope(scope_) : aliases(aliases_)
{} {}
void visitImpl(QueryTreeNodePtr & node) void visitImpl(QueryTreeNodePtr & node)
@ -1034,10 +1099,10 @@ public:
private: private:
void addDuplicatingAlias(const QueryTreeNodePtr & node) void addDuplicatingAlias(const QueryTreeNodePtr & node)
{ {
scope.nodes_with_duplicated_aliases.emplace(node); aliases.nodes_with_duplicated_aliases.emplace(node);
auto cloned_node = node->clone(); auto cloned_node = node->clone();
scope.cloned_nodes_with_duplicated_aliases.emplace_back(cloned_node); aliases.cloned_nodes_with_duplicated_aliases.emplace_back(cloned_node);
scope.nodes_with_duplicated_aliases.emplace(cloned_node); aliases.nodes_with_duplicated_aliases.emplace(cloned_node);
} }
void updateAliasesIfNeeded(const QueryTreeNodePtr & node, bool is_lambda_node) void updateAliasesIfNeeded(const QueryTreeNodePtr & node, bool is_lambda_node)
@ -1053,29 +1118,29 @@ private:
if (is_lambda_node) if (is_lambda_node)
{ {
if (scope.alias_name_to_expression_node->contains(alias)) if (aliases.alias_name_to_expression_node->contains(alias))
addDuplicatingAlias(node); addDuplicatingAlias(node);
auto [_, inserted] = scope.alias_name_to_lambda_node.insert(std::make_pair(alias, node)); auto [_, inserted] = aliases.alias_name_to_lambda_node.insert(std::make_pair(alias, node));
if (!inserted) if (!inserted)
addDuplicatingAlias(node); addDuplicatingAlias(node);
return; return;
} }
if (scope.alias_name_to_lambda_node.contains(alias)) if (aliases.alias_name_to_lambda_node.contains(alias))
addDuplicatingAlias(node); addDuplicatingAlias(node);
auto [_, inserted] = scope.alias_name_to_expression_node->insert(std::make_pair(alias, node)); auto [_, inserted] = aliases.alias_name_to_expression_node->insert(std::make_pair(alias, node));
if (!inserted) if (!inserted)
addDuplicatingAlias(node); addDuplicatingAlias(node);
/// If node is identifier put it also in scope alias name to lambda node map /// If node is identifier put it into transitive aliases map.
if (node->getNodeType() == QueryTreeNodeType::IDENTIFIER) if (const auto * identifier = typeid_cast<const IdentifierNode *>(node.get()))
scope.alias_name_to_lambda_node.insert(std::make_pair(alias, node)); aliases.transitive_aliases.insert(std::make_pair(alias, identifier->getIdentifier()));
} }
IdentifierResolveScope & scope; ScopeAliases & aliases;
}; };
class TableExpressionsAliasVisitor : public InDepthQueryTreeVisitor<TableExpressionsAliasVisitor> class TableExpressionsAliasVisitor : public InDepthQueryTreeVisitor<TableExpressionsAliasVisitor>
@ -1122,7 +1187,7 @@ private:
return; return;
const auto & node_alias = node->getAlias(); const auto & node_alias = node->getAlias();
auto [_, inserted] = scope.alias_name_to_table_expression_node.emplace(node_alias, node); auto [_, inserted] = scope.aliases.alias_name_to_table_expression_node.emplace(node_alias, node);
if (!inserted) if (!inserted)
throw Exception(ErrorCodes::MULTIPLE_EXPRESSIONS_FOR_ALIAS, throw Exception(ErrorCodes::MULTIPLE_EXPRESSIONS_FOR_ALIAS,
"Multiple table expressions with same alias {}. In scope {}", "Multiple table expressions with same alias {}. In scope {}",
@ -1193,7 +1258,7 @@ public:
} }
case QueryTreeNodeType::TABLE_FUNCTION: case QueryTreeNodeType::TABLE_FUNCTION:
{ {
QueryExpressionsAliasVisitor expressions_alias_visitor(scope); QueryExpressionsAliasVisitor expressions_alias_visitor(scope.aliases);
resolveTableFunction(node, scope, expressions_alias_visitor, false /*nested_table_function*/); resolveTableFunction(node, scope, expressions_alias_visitor, false /*nested_table_function*/);
break; break;
} }
@ -1868,7 +1933,7 @@ void QueryAnalyzer::collectScopeValidIdentifiersForTypoCorrection(
if (allow_expression_identifiers) if (allow_expression_identifiers)
{ {
for (const auto & [name, expression] : *scope.alias_name_to_expression_node) for (const auto & [name, expression] : *scope.aliases.alias_name_to_expression_node)
{ {
assert(expression); assert(expression);
auto expression_identifier = Identifier(name); auto expression_identifier = Identifier(name);
@ -1898,13 +1963,13 @@ void QueryAnalyzer::collectScopeValidIdentifiersForTypoCorrection(
{ {
if (allow_function_identifiers) if (allow_function_identifiers)
{ {
for (const auto & [name, _] : *scope.alias_name_to_expression_node) for (const auto & [name, _] : *scope.aliases.alias_name_to_expression_node)
valid_identifiers_result.insert(Identifier(name)); valid_identifiers_result.insert(Identifier(name));
} }
if (allow_table_expression_identifiers) if (allow_table_expression_identifiers)
{ {
for (const auto & [name, _] : scope.alias_name_to_table_expression_node) for (const auto & [name, _] : scope.aliases.alias_name_to_table_expression_node)
valid_identifiers_result.insert(Identifier(name)); valid_identifiers_result.insert(Identifier(name));
} }
} }
@ -2793,21 +2858,7 @@ QueryTreeNodePtr QueryAnalyzer::tryResolveIdentifierFromExpressionArguments(cons
bool QueryAnalyzer::tryBindIdentifierToAliases(const IdentifierLookup & identifier_lookup, const IdentifierResolveScope & scope) bool QueryAnalyzer::tryBindIdentifierToAliases(const IdentifierLookup & identifier_lookup, const IdentifierResolveScope & scope)
{ {
const auto & identifier_bind_part = identifier_lookup.identifier.front(); return scope.aliases.find(identifier_lookup, ScopeAliases::FindOption::FIRST_NAME) != nullptr;
auto get_alias_name_to_node_map = [&]() -> const std::unordered_map<std::string, QueryTreeNodePtr> &
{
if (identifier_lookup.isExpressionLookup())
return *scope.alias_name_to_expression_node;
else if (identifier_lookup.isFunctionLookup())
return scope.alias_name_to_lambda_node;
return scope.alias_name_to_table_expression_node;
};
const auto & alias_name_to_node_map = get_alias_name_to_node_map();
return alias_name_to_node_map.contains(identifier_bind_part);
} }
/** Resolve identifier from scope aliases. /** Resolve identifier from scope aliases.
@ -2857,23 +2908,13 @@ QueryTreeNodePtr QueryAnalyzer::tryResolveIdentifierFromAliases(const Identifier
{ {
const auto & identifier_bind_part = identifier_lookup.identifier.front(); const auto & identifier_bind_part = identifier_lookup.identifier.front();
auto get_alias_name_to_node_map = [&]() -> std::unordered_map<std::string, QueryTreeNodePtr> & auto * it = scope.aliases.find(identifier_lookup, ScopeAliases::FindOption::FIRST_NAME);
{ if (it == nullptr)
if (identifier_lookup.isExpressionLookup())
return *scope.alias_name_to_expression_node;
else if (identifier_lookup.isFunctionLookup())
return scope.alias_name_to_lambda_node;
return scope.alias_name_to_table_expression_node;
};
auto & alias_name_to_node_map = get_alias_name_to_node_map();
auto it = alias_name_to_node_map.find(identifier_bind_part);
if (it == alias_name_to_node_map.end())
return {}; return {};
if (!it->second) QueryTreeNodePtr & alias_node = *it;
if (!alias_node)
throw Exception(ErrorCodes::LOGICAL_ERROR, throw Exception(ErrorCodes::LOGICAL_ERROR,
"Node with alias {} is not valid. In scope {}", "Node with alias {} is not valid. In scope {}",
identifier_bind_part, identifier_bind_part,
@ -2893,14 +2934,14 @@ QueryTreeNodePtr QueryAnalyzer::tryResolveIdentifierFromAliases(const Identifier
return {}; return {};
} }
auto node_type = it->second->getNodeType(); auto node_type = alias_node->getNodeType();
/// Resolve expression if necessary /// Resolve expression if necessary
if (node_type == QueryTreeNodeType::IDENTIFIER) if (node_type == QueryTreeNodeType::IDENTIFIER)
{ {
scope.pushExpressionNode(it->second); scope.pushExpressionNode(alias_node);
auto & alias_identifier_node = it->second->as<IdentifierNode &>(); auto & alias_identifier_node = alias_node->as<IdentifierNode &>();
auto identifier = alias_identifier_node.getIdentifier(); auto identifier = alias_identifier_node.getIdentifier();
auto lookup_result = tryResolveIdentifier(IdentifierLookup{identifier, identifier_lookup.lookup_context}, scope, identifier_resolve_settings); auto lookup_result = tryResolveIdentifier(IdentifierLookup{identifier, identifier_lookup.lookup_context}, scope, identifier_resolve_settings);
if (!lookup_result.resolved_identifier) if (!lookup_result.resolved_identifier)
@ -2916,43 +2957,27 @@ QueryTreeNodePtr QueryAnalyzer::tryResolveIdentifierFromAliases(const Identifier
getHintsErrorMessageSuffix(hints)); getHintsErrorMessageSuffix(hints));
} }
it->second = lookup_result.resolved_identifier; alias_node = lookup_result.resolved_identifier;
/** During collection of aliases if node is identifier and has alias, we cannot say if it is
* column or function node. Check QueryExpressionsAliasVisitor documentation for clarification.
*
* If we resolved identifier node as expression, we must remove identifier node alias from
* function alias map.
* If we resolved identifier node as function, we must remove identifier node alias from
* expression alias map.
*/
if (identifier_lookup.isExpressionLookup())
scope.alias_name_to_lambda_node.erase(identifier_bind_part);
else if (identifier_lookup.isFunctionLookup())
scope.alias_name_to_expression_node->erase(identifier_bind_part);
scope.popExpressionNode(); scope.popExpressionNode();
} }
else if (node_type == QueryTreeNodeType::FUNCTION) else if (node_type == QueryTreeNodeType::FUNCTION)
{ {
resolveExpressionNode(it->second, scope, false /*allow_lambda_expression*/, false /*allow_table_expression*/); resolveExpressionNode(alias_node, scope, false /*allow_lambda_expression*/, false /*allow_table_expression*/);
} }
else if (node_type == QueryTreeNodeType::QUERY || node_type == QueryTreeNodeType::UNION) else if (node_type == QueryTreeNodeType::QUERY || node_type == QueryTreeNodeType::UNION)
{ {
if (identifier_resolve_settings.allow_to_resolve_subquery_during_identifier_resolution) if (identifier_resolve_settings.allow_to_resolve_subquery_during_identifier_resolution)
resolveExpressionNode(it->second, scope, false /*allow_lambda_expression*/, identifier_lookup.isTableExpressionLookup() /*allow_table_expression*/); resolveExpressionNode(alias_node, scope, false /*allow_lambda_expression*/, identifier_lookup.isTableExpressionLookup() /*allow_table_expression*/);
} }
QueryTreeNodePtr result = it->second; if (identifier_lookup.identifier.isCompound() && alias_node)
if (identifier_lookup.identifier.isCompound() && result)
{ {
if (identifier_lookup.isExpressionLookup()) if (identifier_lookup.isExpressionLookup())
{ {
return tryResolveIdentifierFromCompoundExpression( return tryResolveIdentifierFromCompoundExpression(
identifier_lookup.identifier, identifier_lookup.identifier,
1 /*identifier_bind_size*/, 1 /*identifier_bind_size*/,
it->second, alias_node,
{} /* compound_expression_source */, {} /* compound_expression_source */,
scope, scope,
identifier_resolve_settings.allow_to_check_join_tree /* can_be_not_found */); identifier_resolve_settings.allow_to_check_join_tree /* can_be_not_found */);
@ -2967,7 +2992,7 @@ QueryTreeNodePtr QueryAnalyzer::tryResolveIdentifierFromAliases(const Identifier
} }
} }
return result; return alias_node;
} }
/** Resolve identifier from table columns. /** Resolve identifier from table columns.
@ -4128,10 +4153,11 @@ IdentifierResolveResult QueryAnalyzer::tryResolveIdentifier(const IdentifierLook
* SELECT id FROM ( SELECT ... ) AS subquery ARRAY JOIN [0] AS id INNER JOIN second_table USING (id) * SELECT id FROM ( SELECT ... ) AS subquery ARRAY JOIN [0] AS id INNER JOIN second_table USING (id)
* In the example, identifier `id` should be resolved into one from USING (id) column. * In the example, identifier `id` should be resolved into one from USING (id) column.
*/ */
auto alias_it = scope.alias_name_to_expression_node->find(identifier_lookup.identifier.getFullName());
if (alias_it != scope.alias_name_to_expression_node->end() && alias_it->second->getNodeType() == QueryTreeNodeType::COLUMN) auto * alias_it = scope.aliases.find(identifier_lookup, ScopeAliases::FindOption::FULL_NAME);
if (alias_it && (*alias_it)->getNodeType() == QueryTreeNodeType::COLUMN)
{ {
const auto & column_node = alias_it->second->as<ColumnNode &>(); const auto & column_node = (*alias_it)->as<ColumnNode &>();
if (column_node.getColumnSource()->getNodeType() == QueryTreeNodeType::ARRAY_JOIN) if (column_node.getColumnSource()->getNodeType() == QueryTreeNodeType::ARRAY_JOIN)
prefer_column_name_to_alias = true; prefer_column_name_to_alias = true;
} }
@ -5264,7 +5290,7 @@ ProjectionNames QueryAnalyzer::resolveLambda(const QueryTreeNodePtr & lambda_nod
scope.scope_node->formatASTForErrorMessage()); scope.scope_node->formatASTForErrorMessage());
/// Initialize aliases in lambda scope /// Initialize aliases in lambda scope
QueryExpressionsAliasVisitor visitor(scope); QueryExpressionsAliasVisitor visitor(scope.aliases);
visitor.visit(lambda_to_resolve.getExpression()); visitor.visit(lambda_to_resolve.getExpression());
/** Replace lambda arguments with new arguments. /** Replace lambda arguments with new arguments.
@ -5284,8 +5310,8 @@ ProjectionNames QueryAnalyzer::resolveLambda(const QueryTreeNodePtr & lambda_nod
const auto & lambda_argument_name = lambda_argument_identifier ? lambda_argument_identifier->getIdentifier().getFullName() const auto & lambda_argument_name = lambda_argument_identifier ? lambda_argument_identifier->getIdentifier().getFullName()
: lambda_argument_column->getColumnName(); : lambda_argument_column->getColumnName();
bool has_expression_node = scope.alias_name_to_expression_node->contains(lambda_argument_name); bool has_expression_node = scope.aliases.alias_name_to_expression_node->contains(lambda_argument_name);
bool has_alias_node = scope.alias_name_to_lambda_node.contains(lambda_argument_name); bool has_alias_node = scope.aliases.alias_name_to_lambda_node.contains(lambda_argument_name);
if (has_expression_node || has_alias_node) if (has_expression_node || has_alias_node)
{ {
@ -5961,7 +5987,7 @@ ProjectionNames QueryAnalyzer::resolveFunction(QueryTreeNodePtr & node, Identifi
function_names = AggregateFunctionFactory::instance().getAllRegisteredNames(); function_names = AggregateFunctionFactory::instance().getAllRegisteredNames();
possible_function_names.insert(possible_function_names.end(), function_names.begin(), function_names.end()); possible_function_names.insert(possible_function_names.end(), function_names.begin(), function_names.end());
for (auto & [name, lambda_node] : scope.alias_name_to_lambda_node) for (auto & [name, lambda_node] : scope.aliases.alias_name_to_lambda_node)
{ {
if (lambda_node->getNodeType() == QueryTreeNodeType::LAMBDA) if (lambda_node->getNodeType() == QueryTreeNodeType::LAMBDA)
possible_function_names.push_back(name); possible_function_names.push_back(name);
@ -6295,7 +6321,7 @@ ProjectionNames QueryAnalyzer::resolveExpressionNode(QueryTreeNodePtr & node, Id
result_projection_names.push_back(node_alias); result_projection_names.push_back(node_alias);
} }
bool is_duplicated_alias = scope.nodes_with_duplicated_aliases.contains(node); bool is_duplicated_alias = scope.aliases.nodes_with_duplicated_aliases.contains(node);
if (is_duplicated_alias) if (is_duplicated_alias)
scope.non_cached_identifier_lookups_during_expression_resolve.insert({Identifier{node_alias}, IdentifierLookupContext::EXPRESSION}); scope.non_cached_identifier_lookups_during_expression_resolve.insert({Identifier{node_alias}, IdentifierLookupContext::EXPRESSION});
@ -6319,14 +6345,14 @@ ProjectionNames QueryAnalyzer::resolveExpressionNode(QueryTreeNodePtr & node, Id
* *
* To resolve b we need to resolve a. * To resolve b we need to resolve a.
*/ */
auto it = scope.alias_name_to_expression_node->find(node_alias); auto it = scope.aliases.alias_name_to_expression_node->find(node_alias);
if (it != scope.alias_name_to_expression_node->end()) if (it != scope.aliases.alias_name_to_expression_node->end())
node = it->second; node = it->second;
if (allow_lambda_expression) if (allow_lambda_expression)
{ {
it = scope.alias_name_to_lambda_node.find(node_alias); it = scope.aliases.alias_name_to_lambda_node.find(node_alias);
if (it != scope.alias_name_to_lambda_node.end()) if (it != scope.aliases.alias_name_to_lambda_node.end())
node = it->second; node = it->second;
} }
} }
@ -6352,17 +6378,9 @@ ProjectionNames QueryAnalyzer::resolveExpressionNode(QueryTreeNodePtr & node, Id
result_projection_names.push_back(projection_name_it->second); result_projection_names.push_back(projection_name_it->second);
} }
if (resolved_identifier_node && !node_alias.empty())
scope.alias_name_to_lambda_node.erase(node_alias);
if (!resolved_identifier_node && allow_lambda_expression) if (!resolved_identifier_node && allow_lambda_expression)
{
resolved_identifier_node = tryResolveIdentifier({unresolved_identifier, IdentifierLookupContext::FUNCTION}, scope).resolved_identifier; resolved_identifier_node = tryResolveIdentifier({unresolved_identifier, IdentifierLookupContext::FUNCTION}, scope).resolved_identifier;
if (resolved_identifier_node && !node_alias.empty())
scope.alias_name_to_expression_node->erase(node_alias);
}
if (!resolved_identifier_node && allow_table_expression) if (!resolved_identifier_node && allow_table_expression)
{ {
resolved_identifier_node = tryResolveIdentifier({unresolved_identifier, IdentifierLookupContext::TABLE_EXPRESSION}, scope).resolved_identifier; resolved_identifier_node = tryResolveIdentifier({unresolved_identifier, IdentifierLookupContext::TABLE_EXPRESSION}, scope).resolved_identifier;
@ -6601,14 +6619,14 @@ ProjectionNames QueryAnalyzer::resolveExpressionNode(QueryTreeNodePtr & node, Id
*/ */
if (!node_alias.empty() && use_alias_table && !scope.group_by_use_nulls) if (!node_alias.empty() && use_alias_table && !scope.group_by_use_nulls)
{ {
auto it = scope.alias_name_to_expression_node->find(node_alias); auto it = scope.aliases.alias_name_to_expression_node->find(node_alias);
if (it != scope.alias_name_to_expression_node->end()) if (it != scope.aliases.alias_name_to_expression_node->end())
it->second = node; it->second = node;
if (allow_lambda_expression) if (allow_lambda_expression)
{ {
it = scope.alias_name_to_lambda_node.find(node_alias); it = scope.aliases.alias_name_to_lambda_node.find(node_alias);
if (it != scope.alias_name_to_lambda_node.end()) if (it != scope.aliases.alias_name_to_lambda_node.end())
it->second = node; it->second = node;
} }
} }
@ -6981,8 +6999,8 @@ void QueryAnalyzer::initializeQueryJoinTreeNode(QueryTreeNodePtr & join_tree_nod
resolved_identifier = resolved_identifier->clone(); resolved_identifier = resolved_identifier->clone();
/// Update alias name to table expression map /// Update alias name to table expression map
auto table_expression_it = scope.alias_name_to_table_expression_node.find(from_table_identifier_alias); auto table_expression_it = scope.aliases.alias_name_to_table_expression_node.find(from_table_identifier_alias);
if (table_expression_it != scope.alias_name_to_table_expression_node.end()) if (table_expression_it != scope.aliases.alias_name_to_table_expression_node.end())
table_expression_it->second = resolved_identifier; table_expression_it->second = resolved_identifier;
auto table_expression_modifiers = from_table_identifier.getTableExpressionModifiers(); auto table_expression_modifiers = from_table_identifier.getTableExpressionModifiers();
@ -7181,7 +7199,7 @@ void QueryAnalyzer::initializeTableExpressionData(const QueryTreeNodePtr & table
alias_column_resolve_scope.context = scope.context; alias_column_resolve_scope.context = scope.context;
/// Initialize aliases in alias column scope /// Initialize aliases in alias column scope
QueryExpressionsAliasVisitor visitor(alias_column_resolve_scope); QueryExpressionsAliasVisitor visitor(alias_column_resolve_scope.aliases);
visitor.visit(alias_column_to_resolve->getExpression()); visitor.visit(alias_column_to_resolve->getExpression());
resolveExpressionNode(alias_column_resolve_scope.scope_node, resolveExpressionNode(alias_column_resolve_scope.scope_node,
@ -7551,7 +7569,7 @@ void QueryAnalyzer::resolveArrayJoin(QueryTreeNodePtr & array_join_node, Identif
for (auto & array_join_expression : array_join_nodes) for (auto & array_join_expression : array_join_nodes)
{ {
auto array_join_expression_alias = array_join_expression->getAlias(); auto array_join_expression_alias = array_join_expression->getAlias();
if (!array_join_expression_alias.empty() && scope.alias_name_to_expression_node->contains(array_join_expression_alias)) if (!array_join_expression_alias.empty() && scope.aliases.alias_name_to_expression_node->contains(array_join_expression_alias))
throw Exception(ErrorCodes::MULTIPLE_EXPRESSIONS_FOR_ALIAS, throw Exception(ErrorCodes::MULTIPLE_EXPRESSIONS_FOR_ALIAS,
"ARRAY JOIN expression {} with duplicate alias {}. In scope {}", "ARRAY JOIN expression {} with duplicate alias {}. In scope {}",
array_join_expression->formatASTForErrorMessage(), array_join_expression->formatASTForErrorMessage(),
@ -7645,8 +7663,8 @@ void QueryAnalyzer::resolveArrayJoin(QueryTreeNodePtr & array_join_node, Identif
array_join_nodes = std::move(array_join_column_expressions); array_join_nodes = std::move(array_join_column_expressions);
for (auto & array_join_column_expression : array_join_nodes) for (auto & array_join_column_expression : array_join_nodes)
{ {
auto it = scope.alias_name_to_expression_node->find(array_join_column_expression->getAlias()); auto it = scope.aliases.alias_name_to_expression_node->find(array_join_column_expression->getAlias());
if (it != scope.alias_name_to_expression_node->end()) if (it != scope.aliases.alias_name_to_expression_node->end())
{ {
auto & array_join_column_expression_typed = array_join_column_expression->as<ColumnNode &>(); auto & array_join_column_expression_typed = array_join_column_expression->as<ColumnNode &>();
auto array_join_column = std::make_shared<ColumnNode>(array_join_column_expression_typed.getColumn(), auto array_join_column = std::make_shared<ColumnNode>(array_join_column_expression_typed.getColumn(),
@ -7943,7 +7961,7 @@ void QueryAnalyzer::resolveQueryJoinTreeNode(QueryTreeNodePtr & join_tree_node,
if (alias_name.empty()) if (alias_name.empty())
return; return;
auto [it, inserted] = scope.alias_name_to_table_expression_node.emplace(alias_name, table_expression_node); auto [it, inserted] = scope.aliases.alias_name_to_table_expression_node.emplace(alias_name, table_expression_node);
if (!inserted) if (!inserted)
throw Exception(ErrorCodes::MULTIPLE_EXPRESSIONS_FOR_ALIAS, throw Exception(ErrorCodes::MULTIPLE_EXPRESSIONS_FOR_ALIAS,
"Duplicate aliases {} for table expressions in FROM section are not allowed. Try to register {}. Already registered {}.", "Duplicate aliases {} for table expressions in FROM section are not allowed. Try to register {}. Already registered {}.",
@ -8012,7 +8030,7 @@ void QueryAnalyzer::resolveQuery(const QueryTreeNodePtr & query_node, Identifier
throw Exception(ErrorCodes::NOT_IMPLEMENTED, "WITH TOTALS and WITH ROLLUP or CUBE are not supported together in presence of QUALIFY"); throw Exception(ErrorCodes::NOT_IMPLEMENTED, "WITH TOTALS and WITH ROLLUP or CUBE are not supported together in presence of QUALIFY");
/// Initialize aliases in query node scope /// Initialize aliases in query node scope
QueryExpressionsAliasVisitor visitor(scope); QueryExpressionsAliasVisitor visitor(scope.aliases);
if (query_node_typed.hasWith()) if (query_node_typed.hasWith())
visitor.visit(query_node_typed.getWithNode()); visitor.visit(query_node_typed.getWithNode());
@ -8130,7 +8148,7 @@ void QueryAnalyzer::resolveQuery(const QueryTreeNodePtr & query_node, Identifier
table_expressions_visitor.visit(query_node_typed.getJoinTree()); table_expressions_visitor.visit(query_node_typed.getJoinTree());
initializeQueryJoinTreeNode(query_node_typed.getJoinTree(), scope); initializeQueryJoinTreeNode(query_node_typed.getJoinTree(), scope);
scope.alias_name_to_table_expression_node.clear(); scope.aliases.alias_name_to_table_expression_node.clear();
resolveQueryJoinTreeNode(query_node_typed.getJoinTree(), scope, visitor); resolveQueryJoinTreeNode(query_node_typed.getJoinTree(), scope, visitor);
} }
@ -8180,10 +8198,10 @@ void QueryAnalyzer::resolveQuery(const QueryTreeNodePtr & query_node, Identifier
/// Clone is needed cause aliases share subtrees. /// Clone is needed cause aliases share subtrees.
/// If not clone, the same (shared) subtree could be resolved again with different (Nullable) type /// If not clone, the same (shared) subtree could be resolved again with different (Nullable) type
/// See 03023_group_by_use_nulls_analyzer_crashes /// See 03023_group_by_use_nulls_analyzer_crashes
for (auto & [key, node] : scope.alias_name_to_expression_node_before_group_by) for (auto & [key, node] : scope.aliases.alias_name_to_expression_node_before_group_by)
scope.alias_name_to_expression_node_after_group_by[key] = node->clone(); scope.aliases.alias_name_to_expression_node_after_group_by[key] = node->clone();
scope.alias_name_to_expression_node = &scope.alias_name_to_expression_node_after_group_by; scope.aliases.alias_name_to_expression_node = &scope.aliases.alias_name_to_expression_node_after_group_by;
} }
if (query_node_typed.hasHaving()) if (query_node_typed.hasHaving())
@ -8255,7 +8273,7 @@ void QueryAnalyzer::resolveQuery(const QueryTreeNodePtr & query_node, Identifier
* After scope nodes are resolved, we can compare node with duplicate alias with * After scope nodes are resolved, we can compare node with duplicate alias with
* node from scope alias table. * node from scope alias table.
*/ */
for (const auto & node_with_duplicated_alias : scope.cloned_nodes_with_duplicated_aliases) for (const auto & node_with_duplicated_alias : scope.aliases.cloned_nodes_with_duplicated_aliases)
{ {
auto node = node_with_duplicated_alias; auto node = node_with_duplicated_alias;
auto node_alias = node->getAlias(); auto node_alias = node->getAlias();
@ -8266,8 +8284,8 @@ void QueryAnalyzer::resolveQuery(const QueryTreeNodePtr & query_node, Identifier
bool has_node_in_alias_table = false; bool has_node_in_alias_table = false;
auto it = scope.alias_name_to_expression_node->find(node_alias); auto it = scope.aliases.alias_name_to_expression_node->find(node_alias);
if (it != scope.alias_name_to_expression_node->end()) if (it != scope.aliases.alias_name_to_expression_node->end())
{ {
has_node_in_alias_table = true; has_node_in_alias_table = true;
@ -8280,8 +8298,8 @@ void QueryAnalyzer::resolveQuery(const QueryTreeNodePtr & query_node, Identifier
scope.scope_node->formatASTForErrorMessage()); scope.scope_node->formatASTForErrorMessage());
} }
it = scope.alias_name_to_lambda_node.find(node_alias); it = scope.aliases.alias_name_to_lambda_node.find(node_alias);
if (it != scope.alias_name_to_lambda_node.end()) if (it != scope.aliases.alias_name_to_lambda_node.end())
{ {
has_node_in_alias_table = true; has_node_in_alias_table = true;
@ -8326,10 +8344,10 @@ void QueryAnalyzer::resolveQuery(const QueryTreeNodePtr & query_node, Identifier
/// Remove aliases from expression and lambda nodes /// Remove aliases from expression and lambda nodes
for (auto & [_, node] : *scope.alias_name_to_expression_node) for (auto & [_, node] : *scope.aliases.alias_name_to_expression_node)
node->removeAlias(); node->removeAlias();
for (auto & [_, node] : scope.alias_name_to_lambda_node) for (auto & [_, node] : scope.aliases.alias_name_to_lambda_node)
node->removeAlias(); node->removeAlias();
query_node_typed.resolveProjectionColumns(std::move(projection_columns)); query_node_typed.resolveProjectionColumns(std::move(projection_columns));

View File

@ -17,3 +17,4 @@ Alias conflict with identifier inside expression
Alias setting prefer_column_name_to_alias Alias setting prefer_column_name_to_alias
0 0
Value Value
/a/b/c

View File

@ -48,3 +48,5 @@ WITH id AS value SELECT value FROM test_table;
SET prefer_column_name_to_alias = 0; SET prefer_column_name_to_alias = 0;
DROP TABLE test_table; DROP TABLE test_table;
WITH path('clickhouse.com/a/b/c') AS x SELECT x AS path;

View File

@ -93,3 +93,11 @@ SELECT arrayMap(lambda(tuple(x), x + 1), [1, 2, 3]), lambda2(tuple(x), x + 1), 1
DROP TABLE test_table_tuple; DROP TABLE test_table_tuple;
DROP TABLE test_table; DROP TABLE test_table;
WITH x -> (lambda(x) + 1) AS lambda
SELECT lambda(1); -- {serverError UNSUPPORTED_METHOD }
WITH
x -> (lambda1(x) + 1) AS lambda,
lambda AS lambda1
SELECT lambda(1); -- {serverError UNSUPPORTED_METHOD }