@@ -30,7 +30,7 @@ void ASTSelectWithUnionQuery::formatQueryImpl(WriteBuffer & ostr, const FormatSe
3030{
3131 std::string indent_str = settings.one_line ? " " : std::string (4 * frame.indent , ' ' );
3232
33- auto mode_to_str = [&](auto mode)
33+ auto mode_to_str = [&](SelectUnionMode mode)
3434 {
3535 if (mode == SelectUnionMode::UNION_DEFAULT)
3636 return " UNION" ;
@@ -53,26 +53,55 @@ void ASTSelectWithUnionQuery::formatQueryImpl(WriteBuffer & ostr, const FormatSe
5353 return " " ;
5454 };
5555
56+ auto is_except = [](SelectUnionMode mode)
57+ {
58+ return mode == SelectUnionMode::EXCEPT_DEFAULT
59+ || mode == SelectUnionMode::EXCEPT_ALL
60+ || mode == SelectUnionMode::EXCEPT_DISTINCT;
61+ };
62+
63+ auto get_mode = [&](ASTs::const_iterator it)
64+ {
65+ return is_normalized
66+ ? union_mode
67+ : list_of_modes[it - list_of_selects->children .begin () - 1 ];
68+ };
69+
5670 for (ASTs::const_iterator it = list_of_selects->children .begin (); it != list_of_selects->children .end (); ++it)
5771 {
5872 if (it != list_of_selects->children .begin ())
73+ {
5974 ostr << settings.nl_or_ws << indent_str << (settings.hilite ? hilite_keyword : " " )
60- << mode_to_str ((is_normalized) ? union_mode : list_of_modes[it - list_of_selects->children .begin () - 1 ])
61- << (settings.hilite ? hilite_none : " " );
75+ << mode_to_str (get_mode (it))
76+ << (settings.hilite ? hilite_none : " " )
77+ << settings.nl_or_ws ;
78+ }
6279
63- if (auto * /* node*/ _ = (*it)->as <ASTSelectWithUnionQuery>())
64- {
65- if (it != list_of_selects->children .begin ())
66- ostr << settings.nl_or_ws ;
80+ bool need_parens = false ;
6781
82+ // / EXCEPT can be confused with the asterisk modifier:
83+ // / SELECT * EXCEPT SELECT 1 -- two queries
84+ // / SELECT * EXCEPT col -- a modifier for asterisk
85+ // / For this reason, add parentheses when formatting any side of EXCEPT.
86+ ASTs::const_iterator next = it;
87+ ++next;
88+ if ((it != list_of_selects->children .begin () && is_except (get_mode (it)))
89+ || (next != list_of_selects->children .end () && is_except (get_mode (next))))
90+ need_parens = true ;
91+
92+ // / If this is a subtree with another chain of selects, we also need parens.
93+ auto * union_node = (*it)->as <ASTSelectWithUnionQuery>();
94+ if (union_node)
95+ need_parens = true ;
96+
97+ if (need_parens)
98+ {
6899 ostr << indent_str;
69- auto sub_query = std::make_shared<ASTSubquery>(*it);
70- sub_query ->format (ostr, settings, state, frame);
100+ auto subquery = std::make_shared<ASTSubquery>(*it);
101+ subquery ->format (ostr, settings, state, frame);
71102 }
72103 else
73104 {
74- if (it != list_of_selects->children .begin ())
75- ostr << settings.nl_or_ws ;
76105 (*it)->format (ostr, settings, state, frame);
77106 }
78107 }
0 commit comments