1#include <sstream>
2#include <Common/typeid_cast.h>
3#include <Columns/ColumnConst.h>
4#include <DataTypes/DataTypesNumber.h>
5#include <Parsers/IAST.h>
6#include <Parsers/ASTFunction.h>
7#include <Parsers/ASTIdentifier.h>
8#include <Parsers/ASTLiteral.h>
9#include <Parsers/ASTSelectQuery.h>
10#include <Parsers/ASTExpressionList.h>
11#include <Interpreters/SyntaxAnalyzer.h>
12#include <Interpreters/InDepthNodeVisitor.h>
13#include <IO/WriteBufferFromString.h>
14#include <Storages/transformQueryForExternalDatabase.h>
15#include <Storages/MergeTree/KeyCondition.h>
16
17
18namespace DB
19{
20
21namespace ErrorCodes
22{
23 extern const int LOGICAL_ERROR;
24}
25
26namespace
27{
28
29class ReplacingConstantExpressionsMatcher
30{
31public:
32 using Data = Block;
33
34 static bool needChildVisit(ASTPtr &, const ASTPtr &)
35 {
36 return true;
37 }
38
39 static void visit(ASTPtr & node, Block & block_with_constants)
40 {
41 if (!node->as<ASTFunction>())
42 return;
43
44 std::string name = node->getColumnName();
45 if (block_with_constants.has(name))
46 {
47 auto result = block_with_constants.getByName(name);
48 if (!isColumnConst(*result.column))
49 return;
50
51 if (result.column->isNullAt(0))
52 {
53 node = std::make_shared<ASTLiteral>(Field());
54 }
55 else if (isNumber(result.type))
56 {
57 node = std::make_shared<ASTLiteral>(assert_cast<const ColumnConst &>(*result.column).getField());
58 }
59 else
60 {
61 /// Everything except numbers is put as string literal. This is important for Date, DateTime, UUID.
62
63 const IColumn & inner_column = assert_cast<const ColumnConst &>(*result.column).getDataColumn();
64
65 WriteBufferFromOwnString out;
66 result.type->serializeAsText(inner_column, 0, out, FormatSettings());
67 node = std::make_shared<ASTLiteral>(out.str());
68 }
69 }
70 }
71};
72
73void replaceConstantExpressions(ASTPtr & node, const Context & context, const NamesAndTypesList & all_columns)
74{
75 auto syntax_result = SyntaxAnalyzer(context).analyze(node, all_columns);
76 Block block_with_constants = KeyCondition::getBlockWithConstants(node, syntax_result, context);
77
78 InDepthNodeVisitor<ReplacingConstantExpressionsMatcher, true> visitor(block_with_constants);
79 visitor.visit(node);
80}
81
82
83bool isCompatible(const IAST & node)
84{
85 if (const auto * function = node.as<ASTFunction>())
86 {
87 if (function->parameters) /// Parametric aggregate functions
88 return false;
89
90 if (!function->arguments)
91 throw Exception("Logical error: function->arguments is not set", ErrorCodes::LOGICAL_ERROR);
92
93 String name = function->name;
94
95 if (!(name == "and"
96 || name == "or"
97 || name == "not"
98 || name == "equals"
99 || name == "notEquals"
100 || name == "less"
101 || name == "greater"
102 || name == "lessOrEquals"
103 || name == "greaterOrEquals"
104 || name == "like"
105 || name == "notLike"
106 || name == "in"
107 || name == "notIn"
108 || name == "tuple"))
109 return false;
110
111 /// A tuple with zero or one elements is represented by a function tuple(x) and is not compatible,
112 /// but a normal tuple with more than one element is represented as a parenthesed expression (x, y) and is perfectly compatible.
113 if (name == "tuple" && function->arguments->children.size() <= 1)
114 return false;
115
116 for (const auto & expr : function->arguments->children)
117 if (!isCompatible(*expr.get()))
118 return false;
119
120 return true;
121 }
122
123 if (const auto * literal = node.as<ASTLiteral>())
124 {
125 /// Foreign databases often have no support for Array. But Tuple literals are passed to support IN clause.
126 if (literal->value.getType() == Field::Types::Array)
127 return false;
128
129 return true;
130 }
131
132 if (node.as<ASTIdentifier>())
133 return true;
134
135 return false;
136}
137
138}
139
140
141String transformQueryForExternalDatabase(
142 const IAST & query,
143 const NamesAndTypesList & available_columns,
144 IdentifierQuotingStyle identifier_quoting_style,
145 const String & database,
146 const String & table,
147 const Context & context)
148{
149 auto clone_query = query.clone();
150 auto syntax_result = SyntaxAnalyzer(context).analyze(clone_query, available_columns);
151 const Names used_columns = syntax_result->requiredSourceColumns();
152
153 auto select = std::make_shared<ASTSelectQuery>();
154
155 select->replaceDatabaseAndTable(database, table);
156
157 auto select_expr_list = std::make_shared<ASTExpressionList>();
158 for (const auto & name : used_columns)
159 select_expr_list->children.push_back(std::make_shared<ASTIdentifier>(name));
160
161 select->setExpression(ASTSelectQuery::Expression::SELECT, std::move(select_expr_list));
162
163 /** If there was WHERE,
164 * copy it to transformed query if it is compatible,
165 * or if it is AND expression,
166 * copy only compatible parts of it.
167 */
168
169 ASTPtr original_where = clone_query->as<ASTSelectQuery &>().where();
170 if (original_where)
171 {
172 replaceConstantExpressions(original_where, context, available_columns);
173
174 if (isCompatible(*original_where))
175 {
176 select->setExpression(ASTSelectQuery::Expression::WHERE, std::move(original_where));
177 }
178 else if (const auto * function = original_where->as<ASTFunction>())
179 {
180 if (function->name == "and")
181 {
182 bool compatible_found = false;
183 auto new_function_and = makeASTFunction("and");
184 for (const auto & elem : function->arguments->children)
185 {
186 if (isCompatible(*elem))
187 {
188 new_function_and->arguments->children.push_back(elem);
189 compatible_found = true;
190 }
191 }
192 if (new_function_and->arguments->children.size() == 1)
193 new_function_and->name = "";
194
195 if (compatible_found)
196 select->setExpression(ASTSelectQuery::Expression::WHERE, std::move(new_function_and));
197 }
198 }
199 }
200
201 std::stringstream out;
202 IAST::FormatSettings settings(out, true);
203 settings.always_quote_identifiers = true;
204 settings.identifier_quoting_style = identifier_quoting_style;
205
206 select->format(settings);
207
208 return out.str();
209}
210
211}
212