Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Backport #21246 to 21.1: Improve Normalization of ASTSelectWithUnionQ…
…uery
- Loading branch information
robot-clickhouse
committed
Mar 3, 2021
1 parent
a893330
commit 5a46f32
Showing
7 changed files
with
312 additions
and
122 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
116 changes: 116 additions & 0 deletions
116
src/Interpreters/NormalizeSelectWithUnionQueryVisitor.cpp
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,116 @@ | ||
#include <Interpreters/NormalizeSelectWithUnionQueryVisitor.h> | ||
#include <Parsers/ASTExpressionList.h> | ||
#include <Common/typeid_cast.h> | ||
|
||
namespace DB | ||
{ | ||
|
||
namespace ErrorCodes | ||
{ | ||
extern const int EXPECTED_ALL_OR_DISTINCT; | ||
} | ||
|
||
void NormalizeSelectWithUnionQueryMatcher::getSelectsFromUnionListNode(ASTPtr & ast_select, ASTs & selects) | ||
{ | ||
if (auto * inner_union = ast_select->as<ASTSelectWithUnionQuery>()) | ||
{ | ||
for (auto & child : inner_union->list_of_selects->children) | ||
getSelectsFromUnionListNode(child, selects); | ||
|
||
return; | ||
} | ||
|
||
selects.push_back(ast_select); | ||
} | ||
|
||
void NormalizeSelectWithUnionQueryMatcher::visit(ASTPtr & ast, Data & data) | ||
{ | ||
if (auto * select_union = ast->as<ASTSelectWithUnionQuery>()) | ||
visit(*select_union, data); | ||
} | ||
|
||
void NormalizeSelectWithUnionQueryMatcher::visit(ASTSelectWithUnionQuery & ast, Data & data) | ||
{ | ||
auto & union_modes = ast.list_of_modes; | ||
ASTs selects; | ||
auto & select_list = ast.list_of_selects->children; | ||
|
||
int i; | ||
for (i = union_modes.size() - 1; i >= 0; --i) | ||
{ | ||
/// Rewrite UNION Mode | ||
if (union_modes[i] == ASTSelectWithUnionQuery::Mode::Unspecified) | ||
{ | ||
if (data.union_default_mode == UnionMode::ALL) | ||
union_modes[i] = ASTSelectWithUnionQuery::Mode::ALL; | ||
else if (data.union_default_mode == UnionMode::DISTINCT) | ||
union_modes[i] = ASTSelectWithUnionQuery::Mode::DISTINCT; | ||
else | ||
throw Exception( | ||
"Expected ALL or DISTINCT in SelectWithUnion query, because setting (union_default_mode) is empty", | ||
DB::ErrorCodes::EXPECTED_ALL_OR_DISTINCT); | ||
} | ||
|
||
if (union_modes[i] == ASTSelectWithUnionQuery::Mode::ALL) | ||
{ | ||
if (auto * inner_union = select_list[i + 1]->as<ASTSelectWithUnionQuery>(); | ||
inner_union && inner_union->union_mode == ASTSelectWithUnionQuery::Mode::ALL) | ||
{ | ||
/// Inner_union is an UNION ALL list, just lift up | ||
for (auto child = inner_union->list_of_selects->children.rbegin(); child != inner_union->list_of_selects->children.rend(); | ||
++child) | ||
selects.push_back(*child); | ||
} | ||
else | ||
selects.push_back(select_list[i + 1]); | ||
} | ||
/// flatten all left nodes and current node to a UNION DISTINCT list | ||
else if (union_modes[i] == ASTSelectWithUnionQuery::Mode::DISTINCT) | ||
{ | ||
auto distinct_list = std::make_shared<ASTSelectWithUnionQuery>(); | ||
distinct_list->list_of_selects = std::make_shared<ASTExpressionList>(); | ||
distinct_list->children.push_back(distinct_list->list_of_selects); | ||
|
||
for (int j = 0; j <= i + 1; ++j) | ||
{ | ||
getSelectsFromUnionListNode(select_list[j], distinct_list->list_of_selects->children); | ||
} | ||
|
||
distinct_list->union_mode = ASTSelectWithUnionQuery::Mode::DISTINCT; | ||
distinct_list->is_normalized = true; | ||
selects.push_back(std::move(distinct_list)); | ||
break; | ||
} | ||
} | ||
|
||
/// No UNION DISTINCT or only one child in select_list | ||
if (i == -1) | ||
{ | ||
if (auto * inner_union = select_list[0]->as<ASTSelectWithUnionQuery>(); | ||
inner_union && inner_union->union_mode == ASTSelectWithUnionQuery::Mode::ALL) | ||
{ | ||
/// Inner_union is an UNION ALL list, just lift it up | ||
for (auto child = inner_union->list_of_selects->children.rbegin(); child != inner_union->list_of_selects->children.rend(); | ||
++child) | ||
selects.push_back(*child); | ||
} | ||
else | ||
selects.push_back(select_list[0]); | ||
} | ||
|
||
/// Just one union type child, lift it up | ||
if (selects.size() == 1 && selects[0]->as<ASTSelectWithUnionQuery>()) | ||
{ | ||
ast = *(selects[0]->as<ASTSelectWithUnionQuery>()); | ||
return; | ||
} | ||
|
||
// reverse children list | ||
std::reverse(selects.begin(), selects.end()); | ||
|
||
ast.is_normalized = true; | ||
ast.union_mode = ASTSelectWithUnionQuery::Mode::ALL; | ||
|
||
ast.list_of_selects->children = std::move(selects); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,34 @@ | ||
#pragma once | ||
|
||
#include <unordered_set> | ||
|
||
#include <Parsers/IAST.h> | ||
#include <Interpreters/InDepthNodeVisitor.h> | ||
|
||
#include <Core/Settings.h> | ||
#include <Parsers/ASTSelectWithUnionQuery.h> | ||
|
||
namespace DB | ||
{ | ||
|
||
class ASTFunction; | ||
|
||
class NormalizeSelectWithUnionQueryMatcher | ||
{ | ||
public: | ||
struct Data | ||
{ | ||
const UnionMode & union_default_mode; | ||
}; | ||
|
||
static void getSelectsFromUnionListNode(ASTPtr & ast_select, ASTs & selects); | ||
|
||
static void visit(ASTPtr & ast, Data &); | ||
static void visit(ASTSelectWithUnionQuery &, Data &); | ||
static bool needChildVisit(const ASTPtr &, const ASTPtr &) { return true; } | ||
}; | ||
|
||
/// We need normalize children first, so we should visit AST tree bottom up | ||
using NormalizeSelectWithUnionQueryVisitor | ||
= InDepthNodeVisitor<NormalizeSelectWithUnionQueryMatcher, false>; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
66 changes: 66 additions & 0 deletions
66
tests/queries/0_stateless/01732_explain_syntax_union_query.reference
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,66 @@ | ||
SELECT 1 | ||
UNION ALL | ||
SELECT 1 | ||
UNION ALL | ||
SELECT 1 | ||
UNION ALL | ||
SELECT 1 | ||
UNION ALL | ||
SELECT 1 | ||
|
||
SELECT 1 | ||
UNION ALL | ||
( | ||
SELECT 1 | ||
UNION DISTINCT | ||
SELECT 1 | ||
UNION DISTINCT | ||
SELECT 1 | ||
) | ||
UNION ALL | ||
SELECT 1 | ||
|
||
SELECT x | ||
FROM | ||
( | ||
SELECT 1 AS x | ||
UNION ALL | ||
( | ||
SELECT 1 | ||
UNION DISTINCT | ||
SELECT 1 | ||
UNION DISTINCT | ||
SELECT 1 | ||
) | ||
UNION ALL | ||
SELECT 1 | ||
) | ||
|
||
SELECT x | ||
FROM | ||
( | ||
SELECT 1 AS x | ||
UNION ALL | ||
SELECT 1 | ||
UNION ALL | ||
SELECT 1 | ||
) | ||
|
||
SELECT 1 | ||
UNION DISTINCT | ||
SELECT 1 | ||
UNION DISTINCT | ||
SELECT 1 | ||
|
||
SELECT 1 | ||
|
||
|
||
( | ||
SELECT 1 | ||
UNION DISTINCT | ||
SELECT 1 | ||
UNION DISTINCT | ||
SELECT 1 | ||
) | ||
UNION ALL | ||
SELECT 1 |
Oops, something went wrong.