ClickHouse/src/Storages/transformQueryForExternalDatabase.cpp

239 lines
7.3 KiB
C++
Raw Normal View History

2017-12-26 21:34:06 +00:00
#include <Common/typeid_cast.h>
#include <Columns/ColumnConst.h>
#include <DataTypes/DataTypesNumber.h>
2017-12-26 21:34:06 +00:00
#include <Parsers/IAST.h>
#include <Parsers/ASTFunction.h>
#include <Parsers/ASTIdentifier.h>
#include <Parsers/ASTLiteral.h>
#include <Parsers/ASTSelectQuery.h>
#include <Parsers/ASTExpressionList.h>
#include <Interpreters/TreeRewriter.h>
#include <Interpreters/InDepthNodeVisitor.h>
#include <IO/WriteBufferFromString.h>
2017-12-26 21:34:06 +00:00
#include <Storages/transformQueryForExternalDatabase.h>
#include <Storages/MergeTree/KeyCondition.h>
2017-12-26 21:34:06 +00:00
namespace DB
{
2019-05-16 21:09:06 +00:00
namespace ErrorCodes
{
extern const int LOGICAL_ERROR;
}
namespace
{
/// Everything except numbers is put as string literal.
class ReplacingConstantExpressionsMatcherNumOrStr
{
public:
using Data = Block;
static bool needChildVisit(ASTPtr &, const ASTPtr &)
{
return true;
}
static void visit(ASTPtr & node, Block & block_with_constants)
{
if (!node->as<ASTFunction>())
return;
std::string name = node->getColumnName();
if (block_with_constants.has(name))
{
auto result = block_with_constants.getByName(name);
if (!isColumnConst(*result.column))
return;
2018-10-03 10:08:02 +00:00
if (result.column->isNullAt(0))
{
node = std::make_shared<ASTLiteral>(Field());
}
else if (isNumber(result.type))
{
node = std::make_shared<ASTLiteral>(assert_cast<const ColumnConst &>(*result.column).getField());
}
else
{
/// Everything except numbers is put as string literal. This is important for Date, DateTime, UUID.
const IColumn & inner_column = assert_cast<const ColumnConst &>(*result.column).getDataColumn();
WriteBufferFromOwnString out;
result.type->serializeAsText(inner_column, 0, out, FormatSettings());
node = std::make_shared<ASTLiteral>(out.str());
}
}
}
};
class DropAliasesMatcher
{
public:
struct Data {};
Data data;
static bool needChildVisit(ASTPtr &, const ASTPtr &)
{
return true;
}
static void visit(ASTPtr & node, Data)
{
if (!node->tryGetAlias().empty())
node->setAlias({});
}
};
void replaceConstantExpressions(ASTPtr & node, const Context & context, const NamesAndTypesList & all_columns)
{
auto syntax_result = TreeRewriter(context).analyze(node, all_columns);
Block block_with_constants = KeyCondition::getBlockWithConstants(node, syntax_result, context);
InDepthNodeVisitor<ReplacingConstantExpressionsMatcherNumOrStr, true> visitor(block_with_constants);
visitor.visit(node);
}
void dropAliases(ASTPtr & node)
{
DropAliasesMatcher::Data data;
InDepthNodeVisitor<DropAliasesMatcher, true> visitor(data);
visitor.visit(node);
}
bool isCompatible(const IAST & node)
2017-12-26 21:34:06 +00:00
{
2019-03-11 13:22:51 +00:00
if (const auto * function = node.as<ASTFunction>())
2017-12-26 21:34:06 +00:00
{
2019-05-16 22:51:26 +00:00
if (function->parameters) /// Parametric aggregate functions
2019-05-16 21:09:06 +00:00
return false;
if (!function->arguments)
throw Exception("Logical error: function->arguments is not set", ErrorCodes::LOGICAL_ERROR);
2017-12-26 21:34:06 +00:00
String name = function->name;
2019-05-16 21:09:06 +00:00
2017-12-26 21:34:06 +00:00
if (!(name == "and"
|| name == "or"
|| name == "not"
|| name == "equals"
|| name == "notEquals"
2019-05-16 21:09:06 +00:00
|| name == "less"
|| name == "greater"
|| name == "lessOrEquals"
|| name == "greaterOrEquals"
2018-09-20 14:34:30 +00:00
|| name == "like"
|| name == "notLike"
2018-09-21 11:33:58 +00:00
|| name == "in"
2019-05-16 21:09:06 +00:00
|| name == "notIn"
|| name == "tuple"))
return false;
/// A tuple with zero or one elements is represented by a function tuple(x) and is not compatible,
2020-01-11 09:50:41 +00:00
/// but a normal tuple with more than one element is represented as a parenthesized expression (x, y) and is perfectly compatible.
2019-05-16 21:09:06 +00:00
if (name == "tuple" && function->arguments->children.size() <= 1)
2017-12-26 21:34:06 +00:00
return false;
/// If the right hand side of IN is an identifier (example: x IN table), then it's not compatible.
if ((name == "in" || name == "notIn")
&& (function->arguments->children.size() != 2
|| function->arguments->children[1]->as<ASTIdentifier>()))
return false;
2017-12-26 21:34:06 +00:00
for (const auto & expr : function->arguments->children)
2020-03-09 01:22:33 +00:00
if (!isCompatible(*expr))
2017-12-26 21:34:06 +00:00
return false;
return true;
}
2019-03-11 13:22:51 +00:00
if (const auto * literal = node.as<ASTLiteral>())
2017-12-26 21:34:06 +00:00
{
2019-05-16 22:51:26 +00:00
/// Foreign databases often have no support for Array. But Tuple literals are passed to support IN clause.
2020-03-09 01:22:33 +00:00
return literal->value.getType() != Field::Types::Array;
2017-12-26 21:34:06 +00:00
}
2020-03-09 01:22:33 +00:00
return node.as<ASTIdentifier>();
2017-12-26 21:34:06 +00:00
}
}
2017-12-26 21:34:06 +00:00
String transformQueryForExternalDatabase(
2020-03-31 10:52:26 +00:00
const SelectQueryInfo & query_info,
2017-12-26 21:34:06 +00:00
const NamesAndTypesList & available_columns,
IdentifierQuotingStyle identifier_quoting_style,
2017-12-26 21:34:06 +00:00
const String & database,
const String & table,
const Context & context)
{
2020-03-31 10:52:26 +00:00
auto clone_query = query_info.query->clone();
const Names used_columns = query_info.syntax_analyzer_result->requiredSourceColumns();
2017-12-26 21:34:06 +00:00
auto select = std::make_shared<ASTSelectQuery>();
select->replaceDatabaseAndTable(database, table);
auto select_expr_list = std::make_shared<ASTExpressionList>();
for (const auto & name : used_columns)
2018-02-26 03:37:08 +00:00
select_expr_list->children.push_back(std::make_shared<ASTIdentifier>(name));
2017-12-26 21:34:06 +00:00
select->setExpression(ASTSelectQuery::Expression::SELECT, std::move(select_expr_list));
2017-12-26 21:34:06 +00:00
/** If there was WHERE,
* copy it to transformed query if it is compatible,
* or if it is AND expression,
* copy only compatible parts of it.
*/
2019-04-10 11:44:58 +00:00
ASTPtr original_where = clone_query->as<ASTSelectQuery &>().where();
2017-12-26 21:34:06 +00:00
if (original_where)
{
replaceConstantExpressions(original_where, context, available_columns);
2017-12-26 21:34:06 +00:00
if (isCompatible(*original_where))
{
select->setExpression(ASTSelectQuery::Expression::WHERE, std::move(original_where));
2017-12-26 21:34:06 +00:00
}
2019-03-11 13:22:51 +00:00
else if (const auto * function = original_where->as<ASTFunction>())
2017-12-26 21:34:06 +00:00
{
if (function->name == "and")
{
2018-10-29 14:17:58 +00:00
bool compatible_found = false;
auto new_function_and = makeASTFunction("and");
2017-12-26 21:34:06 +00:00
for (const auto & elem : function->arguments->children)
2018-10-29 14:17:58 +00:00
{
2017-12-26 21:34:06 +00:00
if (isCompatible(*elem))
{
new_function_and->arguments->children.push_back(elem);
2018-10-29 14:17:58 +00:00
compatible_found = true;
}
2018-10-29 14:17:58 +00:00
}
if (new_function_and->arguments->children.size() == 1)
new_function_and->name = "";
2017-12-26 21:34:06 +00:00
2018-10-29 14:17:58 +00:00
if (compatible_found)
select->setExpression(ASTSelectQuery::Expression::WHERE, std::move(new_function_and));
2017-12-26 21:34:06 +00:00
}
}
}
ASTPtr select_ptr = select;
dropAliases(select_ptr);
2020-11-09 16:05:40 +00:00
WriteBufferFromOwnString out;
IAST::FormatSettings settings(out, true);
settings.identifier_quoting_style = identifier_quoting_style;
settings.always_quote_identifiers = identifier_quoting_style != IdentifierQuotingStyle::None;
select->format(settings);
2018-09-20 14:34:30 +00:00
return out.str();
2017-12-26 21:34:06 +00:00
}
}