mirror of
https://github.com/ClickHouse/ClickHouse.git
synced 2024-11-27 18:12:02 +00:00
Added subquery_depth support
This commit is contained in:
parent
2b571004f3
commit
ea44b4be5e
@ -93,6 +93,7 @@ namespace ErrorCodes
|
||||
extern const int INVALID_WITH_FILL_EXPRESSION;
|
||||
extern const int INVALID_LIMIT_EXPRESSION;
|
||||
extern const int EMPTY_LIST_OF_COLUMNS_QUERIED;
|
||||
extern const int TOO_DEEP_SUBQUERIES;
|
||||
}
|
||||
|
||||
/** Query analyzer implementation overview. Please check documentation in QueryAnalysisPass.h before.
|
||||
@ -183,7 +184,6 @@ namespace ErrorCodes
|
||||
* TODO: JOIN support SELF JOIN with MergeTree. JOIN support matchers.
|
||||
* TODO: WINDOW functions
|
||||
* TODO: Table expression modifiers final, sample_size, sample_offset
|
||||
* TODO: Scalar subqueries subquery depth
|
||||
* TODO: Support function identifier resolve from parent query scope, if lambda in parent scope does not capture any columns.
|
||||
*/
|
||||
|
||||
@ -547,6 +547,9 @@ struct IdentifierResolveScope
|
||||
/// Node to projection name
|
||||
std::unordered_map<QueryTreeNodePtr, std::string> node_to_projection_name;
|
||||
|
||||
/// Subquery depth
|
||||
size_t subquery_depth = 0;
|
||||
|
||||
TableExpressionData & getTableExpressionDataOrThrow(QueryTreeNodePtr table_expression_node)
|
||||
{
|
||||
auto it = table_expression_node_to_data.find(table_expression_node);
|
||||
@ -826,7 +829,7 @@ private:
|
||||
|
||||
QueryTreeNodePtr tryGetLambdaFromSQLUserDefinedFunction(const std::string & function_name);
|
||||
|
||||
void evaluateScalarSubquery(QueryTreeNodePtr & query_tree_node);
|
||||
void evaluateScalarSubquery(QueryTreeNodePtr & query_tree_node, size_t subquery_depth);
|
||||
|
||||
/// Resolve identifier functions
|
||||
|
||||
@ -942,7 +945,7 @@ QueryTreeNodePtr QueryAnalyzer::tryGetLambdaFromSQLUserDefinedFunction(const std
|
||||
}
|
||||
|
||||
/// Evaluate scalar subquery and perform constant folding.
|
||||
void QueryAnalyzer::evaluateScalarSubquery(QueryTreeNodePtr & node)
|
||||
void QueryAnalyzer::evaluateScalarSubquery(QueryTreeNodePtr & node, size_t subquery_depth)
|
||||
{
|
||||
auto * query_node = node->as<QueryNode>();
|
||||
auto * union_node = node->as<UnionNode>();
|
||||
@ -952,6 +955,10 @@ void QueryAnalyzer::evaluateScalarSubquery(QueryTreeNodePtr & node)
|
||||
node->getNodeTypeName(),
|
||||
node->formatASTForErrorMessage());
|
||||
|
||||
if ((query_node && query_node->hasConstantValue()) ||
|
||||
(union_node && union_node->hasConstantValue()))
|
||||
return;
|
||||
|
||||
auto subquery_context = Context::createCopy(context);
|
||||
Settings subquery_settings = context->getSettings();
|
||||
subquery_settings.max_result_rows = 1;
|
||||
@ -965,9 +972,7 @@ void QueryAnalyzer::evaluateScalarSubquery(QueryTreeNodePtr & node)
|
||||
// context->addScalar(it.first, it.second);
|
||||
}
|
||||
|
||||
size_t subquery_depth = 0;
|
||||
auto options = SelectQueryOptions(QueryProcessingStage::Complete, subquery_depth + 1, true /*is_subqueyr*/);
|
||||
|
||||
auto options = SelectQueryOptions(QueryProcessingStage::Complete, subquery_depth, true /*is_subquery*/);
|
||||
auto interpreter = std::make_unique<InterpreterSelectQueryAnalyzer>(node, options, subquery_context);
|
||||
|
||||
auto io = interpreter->execute();
|
||||
@ -1043,7 +1048,7 @@ void QueryAnalyzer::evaluateScalarSubquery(QueryTreeNodePtr & node)
|
||||
{
|
||||
auto tuple_column = ColumnTuple::create(block.getColumns());
|
||||
tuple_column->get(0, scalar_value);
|
||||
scalar_type = std::make_shared<DataTypeTuple>(block.getDataTypes());
|
||||
scalar_type = std::make_shared<DataTypeTuple>(block.getDataTypes(), block.getNames());
|
||||
}
|
||||
|
||||
auto constant_value = std::make_shared<ConstantValue>(std::move(scalar_value), std::move(scalar_type));
|
||||
@ -1263,21 +1268,18 @@ QueryTreeNodePtr QueryAnalyzer::tryResolveIdentifierFromAliases(const Identifier
|
||||
{
|
||||
resolveFunction(it->second, scope);
|
||||
}
|
||||
else if (node_type == QueryTreeNodeType::QUERY)
|
||||
else if (node_type == QueryTreeNodeType::QUERY || node_type == QueryTreeNodeType::UNION)
|
||||
{
|
||||
IdentifierResolveScope subquery_scope(it->second, &scope /*parent_scope*/);
|
||||
resolveQuery(it->second, subquery_scope);
|
||||
subquery_scope.subquery_depth = scope.subquery_depth + 1;
|
||||
|
||||
if (node_type == QueryTreeNodeType::QUERY)
|
||||
resolveQuery(it->second, subquery_scope);
|
||||
else if (node_type == QueryTreeNodeType::UNION)
|
||||
resolveUnion(it->second, subquery_scope);
|
||||
|
||||
if (identifier_lookup.isExpressionLookup())
|
||||
evaluateScalarSubquery(it->second);
|
||||
}
|
||||
else if (node_type == QueryTreeNodeType::UNION)
|
||||
{
|
||||
IdentifierResolveScope subquery_scope(it->second, &scope /*parent_scope*/);
|
||||
resolveUnion(it->second, subquery_scope);
|
||||
|
||||
if (identifier_lookup.isExpressionLookup())
|
||||
evaluateScalarSubquery(it->second);
|
||||
evaluateScalarSubquery(it->second, subquery_scope.subquery_depth);
|
||||
}
|
||||
|
||||
scope.expressions_in_resolve_process_stack.popNode();
|
||||
@ -2663,15 +2665,15 @@ void QueryAnalyzer::resolveFunction(QueryTreeNodePtr & node, IdentifierResolveSc
|
||||
|
||||
in_second_argument = std::move(in_second_argument_query_node);
|
||||
}
|
||||
else if (query_node)
|
||||
else if (query_node || union_node)
|
||||
{
|
||||
IdentifierResolveScope subquery_scope(in_second_argument, &scope /*parent_scope*/);
|
||||
resolveQuery(in_second_argument, subquery_scope);
|
||||
}
|
||||
else if (union_node)
|
||||
{
|
||||
IdentifierResolveScope subquery_scope(in_second_argument, &scope /*parent_scope*/);
|
||||
resolveUnion(in_second_argument, subquery_scope);
|
||||
subquery_scope.subquery_depth = scope.subquery_depth + 1;
|
||||
|
||||
if (query_node)
|
||||
resolveQuery(in_second_argument, subquery_scope);
|
||||
else if (union_node)
|
||||
resolveUnion(in_second_argument, subquery_scope);
|
||||
}
|
||||
}
|
||||
|
||||
@ -3178,20 +3180,22 @@ void QueryAnalyzer::resolveExpressionNode(QueryTreeNodePtr & node, IdentifierRes
|
||||
case QueryTreeNodeType::QUERY:
|
||||
{
|
||||
IdentifierResolveScope subquery_scope(node, &scope /*parent_scope*/);
|
||||
subquery_scope.subquery_depth = scope.subquery_depth + 1;
|
||||
resolveQuery(node, subquery_scope);
|
||||
|
||||
if (!allow_table_expression)
|
||||
evaluateScalarSubquery(node);
|
||||
evaluateScalarSubquery(node, subquery_scope.subquery_depth);
|
||||
|
||||
break;
|
||||
}
|
||||
case QueryTreeNodeType::UNION:
|
||||
{
|
||||
IdentifierResolveScope subquery_scope(node, &scope /*parent_scope*/);
|
||||
subquery_scope.subquery_depth = scope.subquery_depth + 1;
|
||||
resolveUnion(node, subquery_scope);
|
||||
|
||||
if (!allow_table_expression)
|
||||
evaluateScalarSubquery(node);
|
||||
evaluateScalarSubquery(node, subquery_scope.subquery_depth);
|
||||
|
||||
break;
|
||||
}
|
||||
@ -3893,12 +3897,14 @@ void QueryAnalyzer::resolveQueryJoinTreeNode(QueryTreeNodePtr & join_tree_node,
|
||||
case QueryTreeNodeType::QUERY:
|
||||
{
|
||||
IdentifierResolveScope subquery_scope(join_tree_node, &scope);
|
||||
subquery_scope.subquery_depth = scope.subquery_depth + 1;
|
||||
resolveQuery(join_tree_node, subquery_scope);
|
||||
break;
|
||||
}
|
||||
case QueryTreeNodeType::UNION:
|
||||
{
|
||||
IdentifierResolveScope subquery_scope(join_tree_node, &scope);
|
||||
subquery_scope.subquery_depth = scope.subquery_depth + 1;
|
||||
resolveUnion(join_tree_node, subquery_scope);
|
||||
break;
|
||||
}
|
||||
@ -4132,6 +4138,12 @@ void QueryAnalyzer::resolveQueryJoinTreeNode(QueryTreeNodePtr & join_tree_node,
|
||||
*/
|
||||
void QueryAnalyzer::resolveQuery(const QueryTreeNodePtr & query_node, IdentifierResolveScope & scope)
|
||||
{
|
||||
const auto & settings = context->getSettingsRef();
|
||||
if (settings.max_subquery_depth && scope.subquery_depth > settings.max_subquery_depth)
|
||||
throw Exception(ErrorCodes::TOO_DEEP_SUBQUERIES,
|
||||
"Too deep subqueries. Maximum: {}",
|
||||
settings.max_subquery_depth.toString());
|
||||
|
||||
auto & query_node_typed = query_node->as<QueryNode &>();
|
||||
|
||||
/// Initialize aliases in query node scope
|
||||
|
@ -194,11 +194,6 @@ compound_value Tuple(id UInt64)
|
||||
arrayMap(lambda(tuple(x), toString(compound_value.id)), [1, 2, 3]) Array(String)
|
||||
SELECT '--';
|
||||
--
|
||||
DESCRIBE (SELECT cast(tuple(1), 'Tuple (id UInt64)') AS a, arrayMap(x -> untuple(a), [1,2,3]) FROM test_table);
|
||||
a Tuple(id UInt64)
|
||||
arrayMap(lambda(tuple(x), tupleElement(a, \'id\')), [1, 2, 3]) Array(UInt64)
|
||||
SELECT '--';
|
||||
--
|
||||
DESCRIBE (SELECT cast(tuple(1, 'Value'), 'Tuple (id UInt64, value String)') AS compound_value, arrayMap(x -> compound_value.* EXCEPT value, [1,2,3]));
|
||||
compound_value Tuple(id UInt64, value String)
|
||||
arrayMap(lambda(tuple(x), compound_value.id), [1, 2, 3]) Array(UInt64)
|
||||
@ -256,3 +251,7 @@ DESCRIBE (SELECT arrayMap(x -> (SELECT 1), [1,2,3]), arrayMap(x -> (SELECT 2) AS
|
||||
arrayMap(lambda(tuple(x), _subquery_1), [1, 2, 3]) Array(Nullable(UInt8))
|
||||
arrayMap(a, [1, 2, 3]) Array(Nullable(UInt8))
|
||||
arrayMap(lambda(tuple(x), _subquery_3), [1, 2, 3]) Array(Nullable(UInt8))
|
||||
SELECT '--';
|
||||
--
|
||||
SELECT (SELECT 1 AS a, 2 AS b) AS c, c.a, c.b;
|
||||
(1,2) 1 2
|
||||
|
@ -209,6 +209,10 @@ SELECT '--';
|
||||
|
||||
DESCRIBE (SELECT arrayMap(x -> (SELECT 1), [1,2,3]), arrayMap(x -> (SELECT 2) AS a, [1, 2, 3]), arrayMap(x -> (SELECT 1), [1,2,3]));
|
||||
|
||||
SELECT '--';
|
||||
|
||||
SELECT (SELECT 1 AS a, 2 AS b) AS c, c.a, c.b;
|
||||
|
||||
-- { echoOff }
|
||||
|
||||
DROP TABLE test_table;
|
||||
|
@ -0,0 +1 @@
|
||||
1
|
@ -0,0 +1,4 @@
|
||||
SET use_analyzer = 1;
|
||||
|
||||
SELECT (SELECT a FROM (SELECT 1 AS a)) SETTINGS max_subquery_depth = 1; -- { serverError 162 }
|
||||
SELECT (SELECT a FROM (SELECT 1 AS a)) SETTINGS max_subquery_depth = 2;
|
Loading…
Reference in New Issue
Block a user