SemanticSelectQuery CLICKHOUSE-3996

This commit is contained in:
chertus 2018-12-17 22:30:08 +03:00
parent b38f805097
commit 9ef314aaaf
5 changed files with 81 additions and 49 deletions

View File

@ -1,4 +1,5 @@
#include <Interpreters/JoinToSubqueryTransformVisitor.h>
#include <Interpreters/SemanticSelectQuery.h>
#include <Parsers/ASTSelectQuery.h>
#include <Parsers/ASTTablesInSelectQuery.h>
#include <Parsers/ASTIdentifier.h>
@ -17,31 +18,24 @@ namespace ErrorCodes
extern const int TOO_DEEP_AST;
}
/// Attaches 'with' section to the first visited ASTSelectQuery
struct AppendWithSectionVisitorData
/// Attach additional semantic info to generated select.
struct AppendSemanticVisitorData
{
using TypeToVisit = ASTSelectQuery;
const ASTPtr & with;
const SemanticPtr & semantic;
bool done = false;
void visit(ASTSelectQuery & select, ASTPtr &)
{
if (done || !with)
if (done || !semantic)
return;
if (select.with_expression_list)
{
for (auto & expr : with->children)
select.with_expression_list->children.push_back(expr->clone());
}
else
select.with_expression_list = with->clone();
select.semantic = semantic->clone();
done = true;
}
};
/// Replaces one table element with pair
/// Replaces one table element with pair.
struct RewriteTablesVisitorData
{
using TypeToVisit = ASTTablesInSelectQuery;
@ -61,8 +55,7 @@ struct RewriteTablesVisitorData
}
};
static String getTableNameOrAlias(const ASTPtr & table_element)
static void appendTableNameAndAlias(std::vector<String> & hidden, const ASTPtr & table_element)
{
auto element = static_cast<const ASTTablesInSelectQueryElement *>(table_element.get());
if (!element || element->children.empty())
@ -72,34 +65,14 @@ static String getTableNameOrAlias(const ASTPtr & table_element)
if (!table_expression || table_expression->children.empty())
throw Exception("Expected TableExpression with at least one child", ErrorCodes::LOGICAL_ERROR);
String result = table_expression->children[0]->tryGetAlias();
if (!result.empty())
return result;
String alias = table_expression->children[0]->tryGetAlias();
if (!alias.empty())
hidden.push_back(alias);
auto identifier = static_cast<const ASTIdentifier *>(table_expression->children[0].get());
if (!identifier)
if (!identifier && alias.empty())
throw Exception("Expected Identifier or subquery with alias", ErrorCodes::LOGICAL_ERROR);
return identifier->name;
}
static void addHiddenNames(ASTPtr & with_expression_list, const std::vector<String> & hidden_names, const String & new_name)
{
if (!with_expression_list)
with_expression_list = std::make_shared<ASTExpressionList>();
ParserExpression parser;
for (auto & name : hidden_names)
{
if (name.empty())
continue;
String str_expression = "nameCoalesce(" + name + "," + new_name + ")";
ASTPtr expr = parseQuery(parser, str_expression, 0);
if (!expr)
throw Exception("Cannot parse expression", ErrorCodes::LOGICAL_ERROR);
with_expression_list->children.push_back(expr);
}
hidden.push_back(identifier->name);
}
@ -131,14 +104,17 @@ void JoinToSubqueryTransformMatcher::visit(ASTSelectQuery & select, ASTPtr & ast
for (size_t i = 1; i < num_tables - 1; ++i)
{
ASTPtr right = tables->children[i];
std::vector<String> hidden_names = {getTableNameOrAlias(left), getTableNameOrAlias(right)};
std::vector<String> hidden_names;
appendTableNameAndAlias(hidden_names, left);
appendTableNameAndAlias(hidden_names, right);
String subquery_name = alias_prefix + toString(i);
left = replaceJoin(left, right, select.with_expression_list, subquery_name);
left = replaceJoin(select, left, right, subquery_name);
if (!left)
return;
addHiddenNames(select.with_expression_list, hidden_names, subquery_name);
SemanticSelectQuery::hideNames(select, hidden_names, subquery_name);
}
select.tables = std::make_shared<ASTTablesInSelectQuery>();
@ -149,11 +125,11 @@ void JoinToSubqueryTransformMatcher::visit(ASTSelectQuery & select, ASTPtr & ast
data.done = true;
}
ASTPtr JoinToSubqueryTransformMatcher::replaceJoin(ASTPtr ast_left, ASTPtr ast_right, ASTPtr with, const String & subquery_alias)
ASTPtr JoinToSubqueryTransformMatcher::replaceJoin(ASTSelectQuery & select, ASTPtr ast_left, ASTPtr ast_right, const String & subquery_alias)
{
using RewriteMatcher = LinkedMatcher<
OneTypeMatcher<RewriteTablesVisitorData>,
OneTypeMatcher<AppendWithSectionVisitorData>>;
OneTypeMatcher<AppendSemanticVisitorData>>;
using RewriteVisitor = InDepthNodeVisitor<RewriteMatcher, true>;
auto left = static_cast<const ASTTablesInSelectQueryElement *>(ast_left.get());
@ -175,7 +151,7 @@ ASTPtr JoinToSubqueryTransformMatcher::replaceJoin(ASTPtr ast_left, ASTPtr ast_r
throw Exception("Cannot parse rewrite query", ErrorCodes::LOGICAL_ERROR);
RewriteVisitor::Data visitor_data =
std::make_pair<RewriteTablesVisitorData, AppendWithSectionVisitorData>({ast_left, ast_right}, {with});
std::make_pair<RewriteTablesVisitorData, AppendSemanticVisitorData>({ast_left, ast_right}, {select.semantic});
RewriteVisitor(visitor_data).visit(res);
return res;
}

View File

@ -39,12 +39,10 @@ private:
/// TablesInSelectQueryElement [source1]
/// TablesInSelectQueryElement [source2]
///
/// + WITH nameCoalesce(source1, __join1), nameCoalesce(source2, __join2)
///
static void visit(ASTSelectQuery & select, ASTPtr & ast, Data & data);
/// @return combined TablesInSelectQueryElement or nullptr if cannot rewrite
static ASTPtr replaceJoin(ASTPtr left, ASTPtr right, ASTPtr with, const String & subquery_alias);
static ASTPtr replaceJoin(ASTSelectQuery & select, ASTPtr left, ASTPtr right, const String & subquery_alias);
};
using JoinToSubqueryTransformVisitor = InDepthNodeVisitor<JoinToSubqueryTransformMatcher, true>;

View File

@ -0,0 +1,43 @@
#pragma once
#include <Parsers/IAST.h>
#include <Parsers/ASTSelectQuery.h>
namespace DB
{
/// Additional information for ASTSelectQuery
class SemanticSelectQuery : public ISemantic
{
public:
SemanticPtr clone() const override { return std::make_shared<SemanticSelectQuery>(*this); }
std::vector<String> getPossibleNames(const String & name) const
{
std::vector<String> res;
res.push_back(name);
for (auto it = hidings.find(name); it != hidings.end(); it = hidings.find(it->second))
res.push_back(it->second);
return res;
}
static void hideNames(ASTSelectQuery & select, const std::vector<String> & hidden, const String & new_name)
{
if (!select.semantic)
select.semantic = std::make_shared<SemanticSelectQuery>();
auto & sema = static_cast<SemanticSelectQuery &>(*select.semantic);
sema.hideNames(hidden, new_name);
}
private:
std::unordered_map<String, String> hidings;
void hideNames(const std::vector<String> & hidden, const String & new_name)
{
for (auto & name : hidden)
hidings.emplace(name, new_name);
}
};
}

View File

@ -64,6 +64,8 @@ ASTPtr ASTSelectQuery::clone() const
#undef CLONE
if (semantic)
res->semantic = semantic->clone();
return res;
}

View File

@ -31,6 +31,18 @@ class IAST;
using ASTPtr = std::shared_ptr<IAST>;
using ASTs = std::vector<ASTPtr>;
class ISemantic;
using SemanticPtr = std::shared_ptr<ISemantic>;
/// Interfase to set additional information to IAST. Derived classes should be named according to their AST nodes' types:
/// ASTIdentifier => SemanticIdentifer, ASTSome => SemanticSome, ...
class ISemantic
{
public:
virtual ~ISemantic() = default;
virtual SemanticPtr clone() const = 0;
};
class WriteBuffer;
@ -44,6 +56,7 @@ public:
/// This pointer does not allow it to be deleted while the range refers to it.
StringPtr owned_string;
SemanticPtr semantic;
virtual ~IAST() = default;