11#include < Analyzer/JoinNode.h>
2+ #include < Analyzer/ColumnNode.h>
23#include < Analyzer/ListNode.h>
34#include < Analyzer/Utils.h>
45#include < IO/Operators.h>
@@ -37,6 +38,77 @@ JoinNode::JoinNode(QueryTreeNodePtr left_table_expression_,
3738 children[join_expression_child_index] = std::move (join_expression_);
3839}
3940
41+ // / There is a special workaround for the case when ARRAY JOIN alias is used in USING statement.
42+ // / Example: ... ARRAY JOIN arr AS dummy INNER JOIN system.one USING (dummy);
43+ // /
44+ // / In case of ARRAY JOIN, the column is renamed, so the query tree will look like:
45+ // / JOIN EXPRESSION
46+ // / LIST
47+ // / COLUMN id: 16, column_name: dummy
48+ // / EXPRESSION
49+ // / LIST
50+ // / COLUMN id: 18, column_name: __array_join_exp_1
51+ // / COLUMN id: 19, column_name: dummy
52+ // /
53+ // / Previously, when we convert QueryTree back to ast, the query would look like:
54+ // / ARRAY JOIN arr AS __array_join_exp_1 ALL INNER JOIN system.one USING (__array_join_exp_1)
55+ // / Which is incorrect query (which is broken in distributed case) because system.one do not have __array_join_exp_1.
56+ // /
57+ // / In order to mitigate this, the syntax 'USING (__array_join_exp_1 AS dummy)' is introduced,
58+ // / which means that '__array_join_exp_1' is taken from left, 'dummy' is taken from right,
59+ // / and the USING column name is also 'dummy'
60+ // /
61+ // / See 03448_analyzer_array_join_alias_in_join_using_bug
62+ static ASTPtr tryMakeUsingColumnASTWithAlias (const QueryTreeNodePtr & node)
63+ {
64+ const auto * column_node = node->as <ColumnNode>();
65+ if (!column_node)
66+ return nullptr ;
67+
68+ const auto & expr = column_node->getExpression ();
69+ if (!expr)
70+ return nullptr ;
71+
72+ const auto * expr_list_node = expr->as <ListNode>();
73+ if (!expr_list_node)
74+ return nullptr ;
75+
76+ if (expr_list_node->getNodes ().size () != 2 )
77+ return nullptr ;
78+
79+ const auto * lhs_column_node = expr_list_node->getNodes ()[0 ]->as <ColumnNode>();
80+ const auto * rhs_column_node = expr_list_node->getNodes ()[1 ]->as <ColumnNode>();
81+ if (!lhs_column_node || !rhs_column_node)
82+ return nullptr ;
83+
84+ if (lhs_column_node->getColumnName () == rhs_column_node->getColumnName ())
85+ return nullptr ;
86+
87+ auto node_ast = std::make_shared<ASTIdentifier>(lhs_column_node->getColumnName ());
88+ node_ast->setAlias (rhs_column_node->getColumnName ());
89+ return node_ast;
90+ }
91+
92+ static ASTPtr makeUsingAST (const QueryTreeNodePtr & node)
93+ {
94+ const auto & list_node = node->as <ListNode &>();
95+
96+ auto expr_list = std::make_shared<ASTExpressionList>();
97+ expr_list->children .reserve (list_node.getNodes ().size ());
98+
99+ for (const auto & child : list_node.getNodes ())
100+ {
101+ ASTPtr node_ast = tryMakeUsingColumnASTWithAlias (child);
102+
103+ if (!node_ast)
104+ node_ast = child->toAST ();
105+
106+ expr_list->children .push_back (std::move (node_ast));
107+ }
108+
109+ return expr_list;
110+ }
111+
40112ASTPtr JoinNode::toASTTableJoin () const
41113{
42114 auto join_ast = std::make_shared<ASTTableJoin>();
@@ -46,16 +118,14 @@ ASTPtr JoinNode::toASTTableJoin() const
46118
47119 if (children[join_expression_child_index])
48120 {
49- auto join_expression_ast = children[join_expression_child_index]->toAST ();
50-
51121 if (is_using_join_expression)
52122 {
53- join_ast->using_expression_list = join_expression_ast ;
123+ join_ast->using_expression_list = makeUsingAST (children[join_expression_child_index]) ;
54124 join_ast->children .push_back (join_ast->using_expression_list );
55125 }
56126 else
57127 {
58- join_ast->on_expression = join_expression_ast ;
128+ join_ast->on_expression = children[join_expression_child_index]-> toAST () ;
59129 join_ast->children .push_back (join_ast->on_expression );
60130 }
61131 }
0 commit comments