mirror of
https://github.com/ClickHouse/ClickHouse.git
synced 2024-11-27 10:02:01 +00:00
Improved lambda matcher support
This commit is contained in:
parent
062e17052f
commit
1717d16fa4
@ -591,7 +591,7 @@ private:
|
|||||||
|
|
||||||
QueryTreeNodePtr resolveMatcher(QueryTreeNodePtr & matcher_node, IdentifierResolveScope & scope);
|
QueryTreeNodePtr resolveMatcher(QueryTreeNodePtr & matcher_node, IdentifierResolveScope & scope);
|
||||||
|
|
||||||
void resolveLambda(QueryTreeNodePtr & lambda_node, const QueryTreeNodes & lambda_arguments, IdentifierResolveScope & scope);
|
void resolveLambda(const QueryTreeNodePtr & lambda_node, const QueryTreeNodes & lambda_arguments, IdentifierResolveScope & scope);
|
||||||
|
|
||||||
void resolveFunction(QueryTreeNodePtr & function_node, IdentifierResolveScope & scope);
|
void resolveFunction(QueryTreeNodePtr & function_node, IdentifierResolveScope & scope);
|
||||||
|
|
||||||
@ -603,7 +603,7 @@ private:
|
|||||||
|
|
||||||
void resolveQueryFrom(QueryTreeNodePtr & from_node, IdentifierResolveScope & scope);
|
void resolveQueryFrom(QueryTreeNodePtr & from_node, IdentifierResolveScope & scope);
|
||||||
|
|
||||||
void resolveQuery(QueryTreeNodePtr & query_node, IdentifierResolveScope & scope);
|
void resolveQuery(const QueryTreeNodePtr & query_node, IdentifierResolveScope & scope);
|
||||||
|
|
||||||
/// Query analyzer context
|
/// Query analyzer context
|
||||||
ContextPtr context;
|
ContextPtr context;
|
||||||
@ -1824,6 +1824,22 @@ QueryTreeNodePtr QueryAnalyzer::resolveMatcher(QueryTreeNodePtr & matcher_node,
|
|||||||
IdentifierResolveScope lambda_scope(expression_node, &scope /*parent_scope*/);
|
IdentifierResolveScope lambda_scope(expression_node, &scope /*parent_scope*/);
|
||||||
resolveLambda(lambda_expression_to_resolve, {node}, lambda_scope);
|
resolveLambda(lambda_expression_to_resolve, {node}, lambda_scope);
|
||||||
auto & lambda_expression_to_resolve_typed = lambda_expression_to_resolve->as<LambdaNode &>();
|
auto & lambda_expression_to_resolve_typed = lambda_expression_to_resolve->as<LambdaNode &>();
|
||||||
|
|
||||||
|
if (auto * lambda_list_node_result = lambda_expression_to_resolve_typed.getExpression()->as<ListNode>())
|
||||||
|
{
|
||||||
|
auto & lambda_list_node_result_nodes = lambda_list_node_result->getNodes();
|
||||||
|
size_t lambda_list_node_result_nodes_size = lambda_list_node_result->getNodes().size();
|
||||||
|
|
||||||
|
if (lambda_list_node_result_nodes_size != 1)
|
||||||
|
throw Exception(ErrorCodes::UNSUPPORTED_METHOD,
|
||||||
|
"Lambda in APPLY transformer {} resolved as list node with size {}. Expected 1. In scope {}",
|
||||||
|
apply_transformer->formatASTForErrorMessage(),
|
||||||
|
lambda_list_node_result_nodes_size,
|
||||||
|
scope.scope_node->formatASTForErrorMessage());
|
||||||
|
|
||||||
|
lambda_expression_to_resolve_typed.getExpression() = lambda_list_node_result_nodes[0];
|
||||||
|
}
|
||||||
|
|
||||||
node = lambda_expression_to_resolve_typed.getExpression();
|
node = lambda_expression_to_resolve_typed.getExpression();
|
||||||
}
|
}
|
||||||
else if (apply_transformer->getApplyTransformerType() == ApplyColumnTransformerType::FUNCTION)
|
else if (apply_transformer->getApplyTransformerType() == ApplyColumnTransformerType::FUNCTION)
|
||||||
@ -1948,6 +1964,8 @@ QueryTreeNodePtr QueryAnalyzer::resolveMatcher(QueryTreeNodePtr & matcher_node,
|
|||||||
* This function modified lambda_node during resolve. It is caller responsibility to clone lambda before resolve
|
* This function modified lambda_node during resolve. It is caller responsibility to clone lambda before resolve
|
||||||
* if it is needed for later use.
|
* if it is needed for later use.
|
||||||
*
|
*
|
||||||
|
* Lambda expression can be resolved into list node. It is caller responsibility to handle it properly.
|
||||||
|
*
|
||||||
* lambda_node - node that must have LambdaNode type.
|
* lambda_node - node that must have LambdaNode type.
|
||||||
* arguments - lambda arguments.
|
* arguments - lambda arguments.
|
||||||
* scope - lambda scope. It is client responsibility to create it.
|
* scope - lambda scope. It is client responsibility to create it.
|
||||||
@ -1960,7 +1978,7 @@ QueryTreeNodePtr QueryAnalyzer::resolveMatcher(QueryTreeNodePtr & matcher_node,
|
|||||||
* 5. Resolve lambda body expression.
|
* 5. Resolve lambda body expression.
|
||||||
* 6. Deregister lambda from lambdas in resolve process.
|
* 6. Deregister lambda from lambdas in resolve process.
|
||||||
*/
|
*/
|
||||||
void QueryAnalyzer::resolveLambda(QueryTreeNodePtr & lambda_node, const QueryTreeNodes & lambda_arguments, IdentifierResolveScope & scope)
|
void QueryAnalyzer::resolveLambda(const QueryTreeNodePtr & lambda_node, const QueryTreeNodes & lambda_arguments, IdentifierResolveScope & scope)
|
||||||
{
|
{
|
||||||
auto & lambda = lambda_node->as<LambdaNode &>();
|
auto & lambda = lambda_node->as<LambdaNode &>();
|
||||||
auto & lambda_arguments_nodes = lambda.getArguments().getNodes();
|
auto & lambda_arguments_nodes = lambda.getArguments().getNodes();
|
||||||
@ -1988,7 +2006,7 @@ void QueryAnalyzer::resolveLambda(QueryTreeNodePtr & lambda_node, const QueryTre
|
|||||||
/// Initialize aliases in lambda scope
|
/// Initialize aliases in lambda scope
|
||||||
ScopeAliasVisitorMatcher::Data data{scope};
|
ScopeAliasVisitorMatcher::Data data{scope};
|
||||||
ScopeAliasVisitorMatcher::Visitor visitor(data);
|
ScopeAliasVisitorMatcher::Visitor visitor(data);
|
||||||
visitor.visit(lambda_node);
|
visitor.visit(lambda.getExpression());
|
||||||
|
|
||||||
/** Replace lambda arguments with new arguments.
|
/** Replace lambda arguments with new arguments.
|
||||||
* Additionally validate that there are no aliases with same name as lambda arguments.
|
* Additionally validate that there are no aliases with same name as lambda arguments.
|
||||||
@ -2026,16 +2044,6 @@ void QueryAnalyzer::resolveLambda(QueryTreeNodePtr & lambda_node, const QueryTre
|
|||||||
*/
|
*/
|
||||||
resolveExpressionNode(lambda.getExpression(), scope, false /*allow_lambda_expression*/, false /*allow_table_expression*/);
|
resolveExpressionNode(lambda.getExpression(), scope, false /*allow_lambda_expression*/, false /*allow_table_expression*/);
|
||||||
|
|
||||||
/** TODO: Lambda body can be resolved in expression list. And for standalone lambdas it will work.
|
|
||||||
* TODO: It can potentially be resolved into table or another lambda.
|
|
||||||
* Example: WITH (x -> untuple(x)) AS lambda SELECT untuple(compound_expression).
|
|
||||||
*/
|
|
||||||
// if (lambda.getExpression()->getNodeType() == QueryTreeNodeType::LIST)
|
|
||||||
// throw Exception(ErrorCodes::UNSUPPORTED_METHOD,
|
|
||||||
// "Lambda {} expression body cannot contain list of expressions. In scope {}",
|
|
||||||
// lambda_node->formatASTForErrorMessage(),
|
|
||||||
// scope.scope_node->formatASTForErrorMessage());
|
|
||||||
|
|
||||||
lambdas_in_resolve_process.erase(lambda_node.get());
|
lambdas_in_resolve_process.erase(lambda_node.get());
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -2290,7 +2298,7 @@ void QueryAnalyzer::resolveFunction(QueryTreeNodePtr & node, IdentifierResolveSc
|
|||||||
size_t function_data_type_arguments_size = function_data_type_argument_types.size();
|
size_t function_data_type_arguments_size = function_data_type_argument_types.size();
|
||||||
if (function_data_type_arguments_size != lambda_arguments_size)
|
if (function_data_type_arguments_size != lambda_arguments_size)
|
||||||
throw Exception(ErrorCodes::LOGICAL_ERROR,
|
throw Exception(ErrorCodes::LOGICAL_ERROR,
|
||||||
"Function {} function data type for lambda argument wiht index {} arguments size mismatch. Actual {}. Expected {}. In scope {}",
|
"Function {} function data type for lambda argument with index {} arguments size mismatch. Actual {}. Expected {}. In scope {}",
|
||||||
function_name,
|
function_name,
|
||||||
function_data_type_arguments_size,
|
function_data_type_arguments_size,
|
||||||
lambda_arguments_size,
|
lambda_arguments_size,
|
||||||
@ -2310,6 +2318,20 @@ void QueryAnalyzer::resolveFunction(QueryTreeNodePtr & node, IdentifierResolveSc
|
|||||||
IdentifierResolveScope lambda_scope(lambda_to_resolve, &scope /*parent_scope*/);
|
IdentifierResolveScope lambda_scope(lambda_to_resolve, &scope /*parent_scope*/);
|
||||||
resolveLambda(lambda_to_resolve, lambda_arguments, lambda_scope);
|
resolveLambda(lambda_to_resolve, lambda_arguments, lambda_scope);
|
||||||
|
|
||||||
|
if (auto * lambda_list_node_result = lambda_to_resolve_typed.getExpression()->as<ListNode>())
|
||||||
|
{
|
||||||
|
auto & lambda_list_node_result_nodes = lambda_list_node_result->getNodes();
|
||||||
|
size_t lambda_list_node_result_nodes_size = lambda_list_node_result->getNodes().size();
|
||||||
|
|
||||||
|
if (lambda_list_node_result_nodes_size != 1)
|
||||||
|
throw Exception(ErrorCodes::UNSUPPORTED_METHOD,
|
||||||
|
"Lambda as function argument resolved as list node with size {}. Expected 1. In scope {}",
|
||||||
|
lambda_list_node_result_nodes_size,
|
||||||
|
lambda_to_resolve->formatASTForErrorMessage());
|
||||||
|
|
||||||
|
lambda_to_resolve_typed.getExpression() = lambda_list_node_result_nodes[0];
|
||||||
|
}
|
||||||
|
|
||||||
argument_types[function_lambda_argument_index] = std::make_shared<DataTypeFunction>(function_data_type_argument_types, lambda_to_resolve->getResultType());
|
argument_types[function_lambda_argument_index] = std::make_shared<DataTypeFunction>(function_data_type_argument_types, lambda_to_resolve->getResultType());
|
||||||
argument_columns[function_lambda_argument_index].type = argument_types[function_lambda_argument_index];
|
argument_columns[function_lambda_argument_index].type = argument_types[function_lambda_argument_index];
|
||||||
function_arguments[function_lambda_argument_index] = std::move(lambda_to_resolve);
|
function_arguments[function_lambda_argument_index] = std::move(lambda_to_resolve);
|
||||||
@ -2795,7 +2817,7 @@ void QueryAnalyzer::resolveQueryFrom(QueryTreeNodePtr & from_node, IdentifierRes
|
|||||||
* 5. Remove WITH section from query.
|
* 5. Remove WITH section from query.
|
||||||
* 6. Validate nodes with duplicate aliases.
|
* 6. Validate nodes with duplicate aliases.
|
||||||
*/
|
*/
|
||||||
void QueryAnalyzer::resolveQuery(QueryTreeNodePtr & query_node, IdentifierResolveScope & scope)
|
void QueryAnalyzer::resolveQuery(const QueryTreeNodePtr & query_node, IdentifierResolveScope & scope)
|
||||||
{
|
{
|
||||||
auto & query_node_typed = query_node->as<QueryNode &>();
|
auto & query_node_typed = query_node->as<QueryNode &>();
|
||||||
|
|
||||||
|
@ -18,6 +18,10 @@ value_0_level_0_value_1_level_0
|
|||||||
Lambda matcher
|
Lambda matcher
|
||||||
0
|
0
|
||||||
0 Value
|
0 Value
|
||||||
|
[1,1,1]
|
||||||
|
[2,2,2]
|
||||||
|
0 1 1
|
||||||
|
0 2 2
|
||||||
Lambda untuple
|
Lambda untuple
|
||||||
(1,'Value') 1 Value
|
(1,'Value') 1 Value
|
||||||
Lambda carrying
|
Lambda carrying
|
||||||
|
@ -48,6 +48,14 @@ SELECT 'Lambda matcher';
|
|||||||
WITH x -> * AS lambda SELECT lambda(1);
|
WITH x -> * AS lambda SELECT lambda(1);
|
||||||
WITH x -> * AS lambda SELECT lambda(1) FROM test_table;
|
WITH x -> * AS lambda SELECT lambda(1) FROM test_table;
|
||||||
|
|
||||||
|
WITH cast(tuple(1), 'Tuple (value UInt64)') AS compound_value SELECT arrayMap(x -> compound_value.*, [1,2,3]);
|
||||||
|
WITH cast(tuple(1, 1), 'Tuple (value_1 UInt64, value_2 UInt64)') AS compound_value SELECT arrayMap(x -> compound_value.*, [1,2,3]); -- { serverError 1 }
|
||||||
|
WITH cast(tuple(1, 1), 'Tuple (value_1 UInt64, value_2 UInt64)') AS compound_value SELECT arrayMap(x -> plus(compound_value.*), [1,2,3]);
|
||||||
|
|
||||||
|
WITH cast(tuple(1), 'Tuple (value UInt64)') AS compound_value SELECT id, test_table.* APPLY x -> compound_value.* FROM test_table;
|
||||||
|
WITH cast(tuple(1, 1), 'Tuple (value_1 UInt64, value_2 UInt64)') AS compound_value SELECT id, test_table.* APPLY x -> compound_value.* FROM test_table; -- { serverError 1 }
|
||||||
|
WITH cast(tuple(1, 1), 'Tuple (value_1 UInt64, value_2 UInt64)') AS compound_value SELECT id, test_table.* APPLY x -> plus(compound_value.*) FROM test_table;
|
||||||
|
|
||||||
SELECT 'Lambda untuple';
|
SELECT 'Lambda untuple';
|
||||||
|
|
||||||
WITH x -> untuple(x) AS lambda SELECT cast((1, 'Value'), 'Tuple (id UInt64, value String)') AS value, lambda(value);
|
WITH x -> untuple(x) AS lambda SELECT cast((1, 'Value'), 'Tuple (id UInt64, value String)') AS value, lambda(value);
|
||||||
@ -57,4 +65,5 @@ SELECT 'Lambda carrying';
|
|||||||
WITH functor, x -> functor(x) AS lambda, x -> x + 1 AS functor_1, x -> toString(x) AS functor_2 SELECT lambda(functor_1, 1), lambda(functor_2, 1);
|
WITH functor, x -> functor(x) AS lambda, x -> x + 1 AS functor_1, x -> toString(x) AS functor_2 SELECT lambda(functor_1, 1), lambda(functor_2, 1);
|
||||||
WITH functor, x -> functor(x) AS lambda, x -> x + 1 AS functor_1, x -> toString(x) AS functor_2 SELECT lambda(functor_1, id), lambda(functor_2, id) FROM test_table;
|
WITH functor, x -> functor(x) AS lambda, x -> x + 1 AS functor_1, x -> toString(x) AS functor_2 SELECT lambda(functor_1, id), lambda(functor_2, id) FROM test_table;
|
||||||
|
|
||||||
|
DROP TABLE test_table_tuple;
|
||||||
DROP TABLE test_table;
|
DROP TABLE test_table;
|
||||||
|
Loading…
Reference in New Issue
Block a user