Skip to content

Commit

Permalink
Merge pull request #58654 from yariks5s/paste_join_aliases_requirement
Browse files Browse the repository at this point in the history
Avoid alias duplicates in PASTE JOIN and fix aliases requirement
  • Loading branch information
vdimir committed Jan 22, 2024
2 parents 4d6ef8e + e28fd94 commit 0791c75
Show file tree
Hide file tree
Showing 4 changed files with 85 additions and 1 deletion.
42 changes: 42 additions & 0 deletions src/Analyzer/Passes/QueryAnalysisPass.cpp
Expand Up @@ -1208,6 +1208,8 @@ class QueryAnalyzer

static void validateJoinTableExpressionWithoutAlias(const QueryTreeNodePtr & join_node, const QueryTreeNodePtr & table_expression_node, IdentifierResolveScope & scope);

static void checkDuplicateTableNamesOrAlias(const QueryTreeNodePtr & join_node, QueryTreeNodePtr & left_table_expr, QueryTreeNodePtr & right_table_expr, IdentifierResolveScope & scope);

static std::pair<bool, UInt64> recursivelyCollectMaxOrdinaryExpressions(QueryTreeNodePtr & node, QueryTreeNodes & into);

static void expandGroupByAll(QueryNode & query_tree_node_typed);
Expand Down Expand Up @@ -2244,12 +2246,16 @@ void QueryAnalyzer::validateJoinTableExpressionWithoutAlias(const QueryTreeNodeP
if (table_expression_has_alias)
return;

if (join_node->as<JoinNode &>().getKind() == JoinKind::Paste)
return;

auto * query_node = table_expression_node->as<QueryNode>();
auto * union_node = table_expression_node->as<UnionNode>();
if ((query_node && !query_node->getCTEName().empty()) || (union_node && !union_node->getCTEName().empty()))
return;

auto table_expression_node_type = table_expression_node->getNodeType();

if (table_expression_node_type == QueryTreeNodeType::TABLE_FUNCTION ||
table_expression_node_type == QueryTreeNodeType::QUERY ||
table_expression_node_type == QueryTreeNodeType::UNION)
Expand Down Expand Up @@ -6855,6 +6861,39 @@ void QueryAnalyzer::resolveArrayJoin(QueryTreeNodePtr & array_join_node, Identif
}
}

void QueryAnalyzer::checkDuplicateTableNamesOrAlias(const QueryTreeNodePtr & join_node, QueryTreeNodePtr & left_table_expr, QueryTreeNodePtr & right_table_expr, IdentifierResolveScope & scope)
{
Names column_names;
if (!scope.context->getSettingsRef().joined_subquery_requires_alias)
return;

if (join_node->as<JoinNode &>().getKind() != JoinKind::Paste)
return;

auto * left_node = left_table_expr->as<QueryNode>();
auto * right_node = right_table_expr->as<QueryNode>();

if (!left_node && !right_node)
return;

if (left_node)
for (const auto & name_and_type : left_node->getProjectionColumns())
column_names.push_back(name_and_type.name);
if (right_node)
for (const auto & name_and_type : right_node->getProjectionColumns())
column_names.push_back(name_and_type.name);

if (column_names.empty())
throw Exception(ErrorCodes::LOGICAL_ERROR, "Names of projection columns cannot be empty");

std::sort(column_names.begin(), column_names.end());
for (size_t i = 0; i < column_names.size() - 1; i++) // Check if there is no any duplicates because it will lead to broken result
if (column_names[i] == column_names[i+1])
throw Exception(ErrorCodes::BAD_ARGUMENTS,
"Name of columns and aliases should be unique for this query (you can add/change aliases to avoid duplication)"
"While processing '{}'", join_node->formatASTForErrorMessage());
}

/// Resolve join node in scope
void QueryAnalyzer::resolveJoin(QueryTreeNodePtr & join_node, IdentifierResolveScope & scope, QueryExpressionsAliasVisitor & expressions_visitor)
{
Expand All @@ -6866,6 +6905,9 @@ void QueryAnalyzer::resolveJoin(QueryTreeNodePtr & join_node, IdentifierResolveS
resolveQueryJoinTreeNode(join_node_typed.getRightTableExpression(), scope, expressions_visitor);
validateJoinTableExpressionWithoutAlias(join_node, join_node_typed.getRightTableExpression(), scope);

if (!join_node_typed.getLeftTableExpression()->hasAlias() && !join_node_typed.getRightTableExpression()->hasAlias())
checkDuplicateTableNamesOrAlias(join_node, join_node_typed.getLeftTableExpression(), join_node_typed.getRightTableExpression(), scope);

if (join_node_typed.isOnJoinExpression())
{
expressions_visitor.visit(join_node_typed.getJoinExpression());
Expand Down
1 change: 1 addition & 0 deletions src/Parsers/ExpressionElementParsers.cpp
Expand Up @@ -1451,6 +1451,7 @@ const char * ParserAlias::restricted_keywords[] =
"ASOF",
"BETWEEN",
"CROSS",
"PASTE",
"FINAL",
"FORMAT",
"FROM",
Expand Down
23 changes: 23 additions & 0 deletions tests/queries/0_stateless/02933_paste_join.reference
Expand Up @@ -82,3 +82,26 @@ UInt64
7 2
8 1
9 0
0 0
1 1
2 2
3 3
4 4
5 5
0 0
1 1
2 2
3 3
4 4
5 5
0 0
1 1
2 2
3 3
4 4
5 5
1 2 3
0 0
1 1
0
1
20 changes: 19 additions & 1 deletion tests/queries/0_stateless/02933_paste_join.sql
@@ -1,6 +1,6 @@
select * from (SELECT number as a FROM numbers(10)) t1 PASTE JOIN (select number as a from numbers(10)) t2;
select * from (SELECT number as a FROM numbers(10)) t1 PASTE JOIN (select number as a from numbers(10) order by a desc) t2;
create table if not exists test (num UInt64) engine=Memory;
create table if not exists test (number UInt64) engine=Memory;
insert into test select number from numbers(6);
insert into test select number from numbers(5);
SELECT * FROM (SELECT 1) t1 PASTE JOIN (SELECT 2) SETTINGS joined_subquery_requires_alias=0;
Expand Down Expand Up @@ -35,3 +35,21 @@ SET max_threads = 2;
select * from (SELECT number as a FROM numbers_mt(10)) t1 PASTE JOIN (select number as a from numbers(10) ORDER BY a DESC) t2 SETTINGS max_block_size=10;
select * from (SELECT number as a FROM numbers(10)) t1 ANY PASTE JOIN (select number as a from numbers(10)) t2; -- { clientError SYNTAX_ERROR }
select * from (SELECT number as a FROM numbers(10)) t1 ALL PASTE JOIN (select number as a from numbers(10)) t2; -- { clientError SYNTAX_ERROR }

TRUNCATE TABLE test;
INSERT INTO test SELECT number from numbers(6);
SELECT * FROM (SELECT number FROM test) PASTE JOIN (SELECT number FROM numbers(6) ORDER BY number) SETTINGS joined_subquery_requires_alias = 0;
SELECT * FROM (SELECT number FROM test PASTE JOIN (Select number FROM numbers(7))) PASTE JOIN (SELECT number FROM numbers(6) PASTE JOIN (SELECT number FROM test)) SETTINGS joined_subquery_requires_alias = 0;
SELECT * FROM (SELECT number FROM test PASTE JOIN (SELECT number FROM test PASTE JOIN (Select number FROM numbers(7)))) PASTE JOIN (SELECT number FROM numbers(6) PASTE JOIN (SELECT number FROM test)) SETTINGS joined_subquery_requires_alias = 0;
SELECT * FROM (SELECT 1 AS a) PASTE JOIN (SELECT 2 AS b) PASTE JOIN (SELECT 3 AS c) SETTINGS allow_experimental_analyzer = 1;
SELECT * FROM (SELECT 1 AS a) PASTE JOIN (SELECT 2 AS b) PASTE JOIN (SELECT 3 AS a) SETTINGS allow_experimental_analyzer = 1; -- { serverError AMBIGUOUS_COLUMN_NAME }

SET allow_experimental_analyzer = 1;
CREATE TABLE test1 (a Int32) engine=MergeTree order by a;
INSERT INTO test1 SELECT * FROM numbers(2);
CREATE TABLE test2 (a Int32) engine=MergeTree order by a;
INSERT INTO test2 SELECT * FROM numbers(2);
SELECT * FROM test1 PASTE JOIN (SELECT * FROM test2);
SELECT a `test2.a` FROM test1 PASTE JOIN test2;
SELECT * FROM test1 `test2.a` PASTE JOIN test2 `test2.a`; -- { serverError MULTIPLE_EXPRESSIONS_FOR_ALIAS }
SELECT * FROM test1 PASTE JOIN (SELECT number AS a FROM numbers(2) ORDER BY number DESC); -- { serverError AMBIGUOUS_COLUMN_NAME }

0 comments on commit 0791c75

Please sign in to comment.