Merge pull request #62619 from kitaisreal/analyzer-support-qualify-clause

Analyzer support QUALIFY clause
This commit is contained in:
Alexey Milovidov 2024-04-23 01:05:16 +00:00 committed by GitHub
commit d731838246
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
20 changed files with 272 additions and 14 deletions

View File

@ -21,6 +21,8 @@ SELECT [DISTINCT [ON (column1, column2, ...)]] expr_list
[WHERE expr]
[GROUP BY expr_list] [WITH ROLLUP|WITH CUBE] [WITH TOTALS]
[HAVING expr]
[WINDOW window_expr_list]
[QUALIFY expr]
[ORDER BY expr_list] [WITH FILL] [FROM expr] [TO expr] [STEP expr] [INTERPOLATE [(expr_list)]]
[LIMIT [offset_value, ]n BY columns]
[LIMIT [n, ]m] [WITH TIES]
@ -45,6 +47,7 @@ Specifics of each optional clause are covered in separate sections, which are li
- [GROUP BY clause](../../../sql-reference/statements/select/group-by.md)
- [LIMIT BY clause](../../../sql-reference/statements/select/limit-by.md)
- [HAVING clause](../../../sql-reference/statements/select/having.md)
- [QUALIFY clause](../../../sql-reference/statements/select/qualify.md)
- [LIMIT clause](../../../sql-reference/statements/select/limit.md)
- [OFFSET clause](../../../sql-reference/statements/select/offset.md)
- [UNION clause](../../../sql-reference/statements/select/union.md)

View File

@ -0,0 +1,34 @@
---
slug: /en/sql-reference/statements/select/qualify
sidebar_label: QUALIFY
---
# QUALIFY Clause
Allows filtering window functions results. It is similar to the [WHERE](../../../sql-reference/statements/select/where.md) clause, but the difference is that `WHERE` is performed before window functions evaluation, while `QUALIFY` is performed after it.
It is possible to reference window functions results from `SELECT` clause in `QUALIFY` clause by their alias. Alternatively, `QUALIFY` clause can filter on results of additional window functions that are not returned in query results.
## Limitations
`QUALIFY` cant be used if there are no window functions to evaluate. Use `WHERE` instead.
## Examples
Example:
``` sql
SELECT number, COUNT() OVER (PARTITION BY number % 3) AS partition_count
FROM numbers(10)
QUALIFY partition_count = 4
ORDER BY number;
```
``` text
┌─number─┬─partition_count─┐
│ 0 │ 4 │
│ 3 │ 4 │
│ 6 │ 4 │
│ 9 │ 4 │
└────────┴─────────────────┘
```

View File

@ -7895,6 +7895,9 @@ void QueryAnalyzer::resolveQuery(const QueryTreeNodePtr & query_node, Identifier
if (query_node_typed.hasHaving() && query_node_typed.isGroupByWithTotals() && is_rollup_or_cube)
throw Exception(ErrorCodes::NOT_IMPLEMENTED, "WITH TOTALS and WITH ROLLUP or CUBE are not supported together in presence of HAVING");
if (query_node_typed.hasQualify() && query_node_typed.isGroupByWithTotals() && is_rollup_or_cube)
throw Exception(ErrorCodes::NOT_IMPLEMENTED, "WITH TOTALS and WITH ROLLUP or CUBE are not supported together in presence of QUALIFY");
/// Initialize aliases in query node scope
QueryExpressionsAliasVisitor visitor(scope);
@ -7919,6 +7922,9 @@ void QueryAnalyzer::resolveQuery(const QueryTreeNodePtr & query_node, Identifier
if (query_node_typed.hasWindow())
visitor.visit(query_node_typed.getWindowNode());
if (query_node_typed.hasQualify())
visitor.visit(query_node_typed.getQualify());
if (query_node_typed.hasOrderBy())
visitor.visit(query_node_typed.getOrderByNode());
@ -8067,6 +8073,9 @@ void QueryAnalyzer::resolveQuery(const QueryTreeNodePtr & query_node, Identifier
if (query_node_typed.hasWindow())
resolveWindowNodeList(query_node_typed.getWindowNode(), scope);
if (query_node_typed.hasQualify())
resolveExpressionNode(query_node_typed.getQualify(), scope, false /*allow_lambda_expression*/, false /*allow_table_expression*/);
if (query_node_typed.hasOrderBy())
{
replaceNodesWithPositionalArguments(query_node_typed.getOrderByNode(), query_node_typed.getProjection().getNodes(), scope);

View File

@ -197,6 +197,12 @@ void QueryNode::dumpTreeImpl(WriteBuffer & buffer, FormatState & format_state, s
getWindow().dumpTreeImpl(buffer, format_state, indent + 4);
}
if (hasQualify())
{
buffer << '\n' << std::string(indent + 2, ' ') << "QUALIFY\n";
getQualify()->dumpTreeImpl(buffer, format_state, indent + 4);
}
if (hasOrderBy())
{
buffer << '\n' << std::string(indent + 2, ' ') << "ORDER BY\n";
@ -381,6 +387,9 @@ ASTPtr QueryNode::toASTImpl(const ConvertToASTOptions & options) const
if (hasWindow())
select_query->setExpression(ASTSelectQuery::Expression::WINDOW, getWindow().toAST(options));
if (hasQualify())
select_query->setExpression(ASTSelectQuery::Expression::QUALIFY, getQualify()->toAST(options));
if (hasOrderBy())
select_query->setExpression(ASTSelectQuery::Expression::ORDER_BY, getOrderBy().toAST(options));

View File

@ -416,6 +416,24 @@ public:
return children[window_child_index];
}
/// Returns true if query node QUALIFY section is not empty, false otherwise
bool hasQualify() const
{
return getQualify() != nullptr;
}
/// Get QUALIFY section node
const QueryTreeNodePtr & getQualify() const
{
return children[qualify_child_index];
}
/// Get QUALIFY section node
QueryTreeNodePtr & getQualify()
{
return children[qualify_child_index];
}
/// Returns true if query node ORDER BY section is not empty, false otherwise
bool hasOrderBy() const
{
@ -622,13 +640,14 @@ private:
static constexpr size_t group_by_child_index = 5;
static constexpr size_t having_child_index = 6;
static constexpr size_t window_child_index = 7;
static constexpr size_t order_by_child_index = 8;
static constexpr size_t interpolate_child_index = 9;
static constexpr size_t limit_by_limit_child_index = 10;
static constexpr size_t limit_by_offset_child_index = 11;
static constexpr size_t limit_by_child_index = 12;
static constexpr size_t limit_child_index = 13;
static constexpr size_t offset_child_index = 14;
static constexpr size_t qualify_child_index = 8;
static constexpr size_t order_by_child_index = 9;
static constexpr size_t interpolate_child_index = 10;
static constexpr size_t limit_by_limit_child_index = 11;
static constexpr size_t limit_by_offset_child_index = 12;
static constexpr size_t limit_by_child_index = 13;
static constexpr size_t limit_child_index = 14;
static constexpr size_t offset_child_index = 15;
static constexpr size_t children_size = offset_child_index + 1;
};

View File

@ -330,6 +330,10 @@ QueryTreeNodePtr QueryTreeBuilder::buildSelectExpression(const ASTPtr & select_q
if (window_list)
current_query_tree->getWindowNode() = buildWindowList(window_list, current_context);
auto qualify_expression = select_query_typed.qualify();
if (qualify_expression)
current_query_tree->getQualify() = buildExpression(qualify_expression, current_context);
auto select_order_by_list = select_query_typed.orderBy();
if (select_order_by_list)
current_query_tree->getOrderByNode() = buildSortList(select_order_by_list, current_context);

View File

@ -56,6 +56,9 @@ void validateFilters(const QueryTreeNodePtr & query_node)
if (query_node_typed.hasHaving())
validateFilter(query_node_typed.getHaving(), "HAVING", query_node);
if (query_node_typed.hasQualify())
validateFilter(query_node_typed.getQualify(), "QUALIFY", query_node);
}
namespace

View File

@ -47,7 +47,7 @@ Suggest::Suggest()
"GRANT", "REVOKE", "OPTION", "ADMIN", "EXCEPT", "REPLACE", "IDENTIFIED", "HOST",
"NAME", "READONLY", "WRITABLE", "PERMISSIVE", "FOR", "RESTRICTIVE", "RANDOMIZED", "INTERVAL",
"LIMITS", "ONLY", "TRACKING", "IP", "REGEXP", "ILIKE", "CLEANUP", "APPEND",
"IGNORE NULLS", "RESPECT NULLS", "OVER", "PASTE"});
"IGNORE NULLS", "RESPECT NULLS", "OVER", "PASTE", "WINDOW", "QUALIFY"});
}
static String getLoadSuggestionQuery(Int32 suggestion_limit, bool basic_suggestion)

View File

@ -144,6 +144,12 @@ void ASTSelectQuery::formatImpl(const FormatSettings & s, FormatState & state, F
window()->as<ASTExpressionList &>().formatImplMultiline(s, state, frame);
}
if (qualify())
{
s.ostr << (s.hilite ? hilite_keyword : "") << s.nl_or_ws << indent_str << "QUALIFY " << (s.hilite ? hilite_none : "");
qualify()->formatImpl(s, state, frame);
}
if (!order_by_all && orderBy())
{
s.ostr << (s.hilite ? hilite_keyword : "") << s.nl_or_ws << indent_str << "ORDER BY" << (s.hilite ? hilite_none : "");

View File

@ -25,6 +25,7 @@ public:
GROUP_BY,
HAVING,
WINDOW,
QUALIFY,
ORDER_BY,
LIMIT_BY_OFFSET,
LIMIT_BY_LENGTH,
@ -55,6 +56,8 @@ public:
return "HAVING";
case Expression::WINDOW:
return "WINDOW";
case Expression::QUALIFY:
return "QUALIFY";
case Expression::ORDER_BY:
return "ORDER BY";
case Expression::LIMIT_BY_OFFSET:
@ -95,6 +98,7 @@ public:
ASTPtr & refPrewhere() { return getExpression(Expression::PREWHERE); }
ASTPtr & refWhere() { return getExpression(Expression::WHERE); }
ASTPtr & refHaving() { return getExpression(Expression::HAVING); }
ASTPtr & refQualify() { return getExpression(Expression::QUALIFY); }
ASTPtr with() const { return getExpression(Expression::WITH); }
ASTPtr select() const { return getExpression(Expression::SELECT); }
@ -104,6 +108,7 @@ public:
ASTPtr groupBy() const { return getExpression(Expression::GROUP_BY); }
ASTPtr having() const { return getExpression(Expression::HAVING); }
ASTPtr window() const { return getExpression(Expression::WINDOW); }
ASTPtr qualify() const { return getExpression(Expression::QUALIFY); }
ASTPtr orderBy() const { return getExpression(Expression::ORDER_BY); }
ASTPtr limitByOffset() const { return getExpression(Expression::LIMIT_BY_OFFSET); }
ASTPtr limitByLength() const { return getExpression(Expression::LIMIT_BY_LENGTH); }
@ -113,7 +118,7 @@ public:
ASTPtr settings() const { return getExpression(Expression::SETTINGS); }
ASTPtr interpolate() const { return getExpression(Expression::INTERPOLATE); }
bool hasFiltration() const { return where() || prewhere() || having(); }
bool hasFiltration() const { return where() || prewhere() || having() || qualify(); }
/// Set/Reset/Remove expression.
void setExpression(Expression expr, ASTPtr && ast);

View File

@ -507,6 +507,7 @@ namespace DB
MR_MACROS(WHEN, "WHEN") \
MR_MACROS(WHERE, "WHERE") \
MR_MACROS(WINDOW, "WINDOW") \
MR_MACROS(QUALIFY, "QUALIFY") \
MR_MACROS(WITH_ADMIN_OPTION, "WITH ADMIN OPTION") \
MR_MACROS(WITH_CHECK, "WITH CHECK") \
MR_MACROS(WITH_FILL, "WITH FILL") \

View File

@ -1481,6 +1481,7 @@ const char * ParserAlias::restricted_keywords[] =
"USING",
"WHERE",
"WINDOW",
"QUALIFY",
"WITH",
"INTERSECT",
"EXCEPT",

View File

@ -49,6 +49,7 @@ bool ParserSelectQuery::parseImpl(Pos & pos, ASTPtr & node, Expected & expected)
ParserKeyword s_totals(Keyword::TOTALS);
ParserKeyword s_having(Keyword::HAVING);
ParserKeyword s_window(Keyword::WINDOW);
ParserKeyword s_qualify(Keyword::QUALIFY);
ParserKeyword s_order_by(Keyword::ORDER_BY);
ParserKeyword s_limit(Keyword::LIMIT);
ParserKeyword s_settings(Keyword::SETTINGS);
@ -86,6 +87,7 @@ bool ParserSelectQuery::parseImpl(Pos & pos, ASTPtr & node, Expected & expected)
ASTPtr group_expression_list;
ASTPtr having_expression;
ASTPtr window_list;
ASTPtr qualify_expression;
ASTPtr order_expression_list;
ASTPtr interpolate_expression_list;
ASTPtr limit_by_length;
@ -266,6 +268,13 @@ bool ParserSelectQuery::parseImpl(Pos & pos, ASTPtr & node, Expected & expected)
}
}
/// QUALIFY expr
if (s_qualify.ignore(pos, expected))
{
if (!exp_elem.parse(pos, qualify_expression, expected))
return false;
}
/// ORDER BY expr ASC|DESC COLLATE 'locale' list
if (s_order_by.ignore(pos, expected))
{
@ -489,6 +498,7 @@ bool ParserSelectQuery::parseImpl(Pos & pos, ASTPtr & node, Expected & expected)
select_query->setExpression(ASTSelectQuery::Expression::GROUP_BY, std::move(group_expression_list));
select_query->setExpression(ASTSelectQuery::Expression::HAVING, std::move(having_expression));
select_query->setExpression(ASTSelectQuery::Expression::WINDOW, std::move(window_list));
select_query->setExpression(ASTSelectQuery::Expression::QUALIFY, std::move(qualify_expression));
select_query->setExpression(ASTSelectQuery::Expression::ORDER_BY, std::move(order_expression_list));
select_query->setExpression(ASTSelectQuery::Expression::LIMIT_BY_OFFSET, std::move(limit_by_offset));
select_query->setExpression(ASTSelectQuery::Expression::LIMIT_BY_LENGTH, std::move(limit_by_length));

View File

@ -1367,6 +1367,16 @@ void Planner::buildPlanForQueryNode()
select_query_info.has_aggregates = hasAggregateFunctionNodes(query_tree);
select_query_info.need_aggregate = query_node.hasGroupBy() || select_query_info.has_aggregates;
if (!select_query_info.has_window && query_node.hasQualify())
{
if (query_node.hasHaving())
query_node.getHaving() = mergeConditionNodes({query_node.getHaving(), query_node.getQualify()}, query_context);
else
query_node.getHaving() = query_node.getQualify();
query_node.getQualify() = {};
}
if (!select_query_info.need_aggregate && query_node.hasHaving())
{
if (query_node.hasWhere())
@ -1636,6 +1646,9 @@ void Planner::buildPlanForQueryNode()
addWindowSteps(query_plan, planner_context, window_analysis_result);
}
if (expression_analysis_result.hasQualify())
addFilterStep(query_plan, expression_analysis_result.getQualify(), "QUALIFY", result_actions_to_execute);
const auto & projection_analysis_result = expression_analysis_result.getProjection();
addExpressionStep(query_plan, projection_analysis_result.projection_actions, "Projection", result_actions_to_execute);

View File

@ -513,6 +513,16 @@ PlannerExpressionsAnalysisResult buildExpressionAnalysisResult(const QueryTreeNo
if (window_analysis_result_optional)
current_output_columns = actions_chain.getLastStepAvailableOutputColumns();
std::optional<FilterAnalysisResult> qualify_analysis_result_optional;
std::optional<size_t> qualify_action_step_index_optional;
if (query_node.hasQualify())
{
qualify_analysis_result_optional = analyzeFilter(query_node.getQualify(), current_output_columns, planner_context, actions_chain);
qualify_action_step_index_optional = actions_chain.getLastStepIndex();
current_output_columns = actions_chain.getLastStepAvailableOutputColumns();
}
auto projection_analysis_result = analyzeProjection(query_node, current_output_columns, planner_context, actions_chain);
current_output_columns = actions_chain.getLastStepAvailableOutputColumns();
@ -604,7 +614,7 @@ PlannerExpressionsAnalysisResult buildExpressionAnalysisResult(const QueryTreeNo
PlannerExpressionsAnalysisResult expressions_analysis_result(std::move(projection_analysis_result));
if (where_action_step_index_optional && where_analysis_result_optional)
if (where_analysis_result_optional && where_action_step_index_optional)
{
auto & where_analysis_result = *where_analysis_result_optional;
auto & where_actions_chain_node = actions_chain.at(*where_action_step_index_optional);
@ -615,7 +625,7 @@ PlannerExpressionsAnalysisResult buildExpressionAnalysisResult(const QueryTreeNo
if (aggregation_analysis_result_optional)
expressions_analysis_result.addAggregation(std::move(*aggregation_analysis_result_optional));
if (having_action_step_index_optional && having_analysis_result_optional)
if (having_analysis_result_optional && having_action_step_index_optional)
{
auto & having_analysis_result = *having_analysis_result_optional;
auto & having_actions_chain_node = actions_chain.at(*having_action_step_index_optional);
@ -626,6 +636,14 @@ PlannerExpressionsAnalysisResult buildExpressionAnalysisResult(const QueryTreeNo
if (window_analysis_result_optional)
expressions_analysis_result.addWindow(std::move(*window_analysis_result_optional));
if (qualify_analysis_result_optional && qualify_action_step_index_optional)
{
auto & qualify_analysis_result = *qualify_analysis_result_optional;
auto & qualify_actions_chain_node = actions_chain.at(*qualify_action_step_index_optional);
qualify_analysis_result.remove_filter_column = !qualify_actions_chain_node->getChildRequiredOutputColumnsNames().contains(qualify_analysis_result.filter_column_name);
expressions_analysis_result.addQualify(std::move(qualify_analysis_result));
}
if (sort_analysis_result_optional)
expressions_analysis_result.addSort(std::move(*sort_analysis_result_optional));

View File

@ -129,6 +129,21 @@ public:
window_analysis_result = std::move(window_analysis_result_);
}
bool hasQualify() const
{
return qualify_analysis_result.filter_actions != nullptr;
}
const FilterAnalysisResult & getQualify() const
{
return qualify_analysis_result;
}
void addQualify(FilterAnalysisResult qualify_analysis_result_)
{
qualify_analysis_result = std::move(qualify_analysis_result_);
}
bool hasSort() const
{
return sort_analysis_result.before_order_by_actions != nullptr;
@ -165,6 +180,7 @@ private:
AggregationAnalysisResult aggregation_analysis_result;
FilterAnalysisResult having_analysis_result;
WindowAnalysisResult window_analysis_result;
FilterAnalysisResult qualify_analysis_result;
SortAnalysisResult sort_analysis_result;
LimitByAnalysisResult limit_by_analysis_result;
};

View File

@ -1,5 +1,4 @@
aaa
(1,1) (1,1)
1
a1 1
1

View File

@ -16,8 +16,6 @@ SELECT __getScalar(); -- { serverError NUMBER_OF_ARGUMENTS_DOESNT_MATCH }
SELECT __getScalar(1); -- { serverError ILLEGAL_TYPE_OF_ARGUMENT }
SELECT __getScalar(materialize('1')); -- { serverError ILLEGAL_TYPE_OF_ARGUMENT }
WITH ( SELECT (1,1) ) as a SELECT materialize(a), __getScalar('17789833925953107877_7493841889429261611') SETTINGS allow_experimental_analyzer = 1;
SELECT __scalarSubqueryResult('1');
SELECT 'a' || __scalarSubqueryResult(a), materialize('1') as a;
SELECT __scalarSubqueryResult(a, a), materialize('1') as a; -- { serverError NUMBER_OF_ARGUMENTS_DOESNT_MATCH }

View File

@ -0,0 +1,74 @@
0 4
3 4
6 4
9 4
--
0
3
6
9
--
6
7
8
9
--
0 5
--
0 5
--
0 4
3 4
6 4
9 4
--
Expression (Project names)
Header: number UInt64
partition_count UInt64
Actions: INPUT : 0 -> __table1.number UInt64 : 0
INPUT : 1 -> count() OVER (PARTITION BY modulo(__table1.number, 3_UInt8)) UInt64 : 1
ALIAS __table1.number :: 0 -> number UInt64 : 2
ALIAS count() OVER (PARTITION BY modulo(__table1.number, 3_UInt8)) :: 1 -> partition_count UInt64 : 0
Positions: 2 0
Sorting (Sorting for ORDER BY)
Header: __table1.number UInt64
count() OVER (PARTITION BY modulo(__table1.number, 3_UInt8)) UInt64
Sort description: __table1.number ASC
Expression ((Before ORDER BY + Projection))
Header: __table1.number UInt64
count() OVER (PARTITION BY modulo(__table1.number, 3_UInt8)) UInt64
Actions: INPUT :: 0 -> __table1.number UInt64 : 0
INPUT :: 1 -> count() OVER (PARTITION BY modulo(__table1.number, 3_UInt8)) UInt64 : 1
Positions: 0 1
Filter (QUALIFY)
Header: __table1.number UInt64
count() OVER (PARTITION BY modulo(__table1.number, 3_UInt8)) UInt64
Filter column: equals(count() OVER (PARTITION BY modulo(__table1.number, 3_UInt8)), 4_UInt8) (removed)
Actions: INPUT :: 0 -> __table1.number UInt64 : 0
INPUT :: 1 -> count() OVER (PARTITION BY modulo(__table1.number, 3_UInt8)) UInt64 : 1
INPUT : 2 -> count() OVER (PARTITION BY modulo(__table1.number, 3_UInt8)) UInt64 : 2
COLUMN Const(UInt8) -> 4_UInt8 UInt8 : 3
FUNCTION equals(count() OVER (PARTITION BY modulo(__table1.number, 3_UInt8)) :: 2, 4_UInt8 :: 3) -> equals(count() OVER (PARTITION BY modulo(__table1.number, 3_UInt8)), 4_UInt8) UInt8 : 4
Positions: 4 0 1
Window (Window step for window \'PARTITION BY modulo(__table1.number, 3_UInt8)\')
Header: modulo(__table1.number, 3_UInt8) UInt8
__table1.number UInt64
count() OVER (PARTITION BY modulo(__table1.number, 3_UInt8)) UInt64
count() OVER (PARTITION BY modulo(__table1.number, 3_UInt8)) UInt64
Window: (PARTITION BY modulo(__table1.number, 3_UInt8))
Functions: count() OVER (PARTITION BY modulo(__table1.number, 3_UInt8))
count() OVER (PARTITION BY modulo(__table1.number, 3_UInt8))
Sorting (Sorting for window \'PARTITION BY modulo(__table1.number, 3_UInt8)\')
Header: modulo(__table1.number, 3_UInt8) UInt8
__table1.number UInt64
Sort description: modulo(__table1.number, 3_UInt8) ASC
Expression ((Before WINDOW + Change column names to column identifiers))
Header: modulo(__table1.number, 3_UInt8) UInt8
__table1.number UInt64
Actions: INPUT : 0 -> number UInt64 : 0
COLUMN Const(UInt8) -> 3_UInt8 UInt8 : 1
ALIAS number :: 0 -> __table1.number UInt64 : 2
FUNCTION modulo(__table1.number : 2, 3_UInt8 :: 1) -> modulo(__table1.number, 3_UInt8) UInt8 : 0
Positions: 0 2
ReadFromSystemNumbers
Header: number UInt64

View File

@ -0,0 +1,36 @@
SET allow_experimental_analyzer = 1;
SELECT number, COUNT() OVER (PARTITION BY number % 3) AS partition_count FROM numbers(10) QUALIFY partition_count = 4 ORDER BY number;
SELECT '--';
SELECT number FROM numbers(10) QUALIFY (COUNT() OVER (PARTITION BY number % 3) AS partition_count) = 4 ORDER BY number;
SELECT '--';
SELECT number FROM numbers(10) QUALIFY number > 5 ORDER BY number;
SELECT '--';
SELECT (number % 2) AS key, count() FROM numbers(10) GROUP BY key HAVING key = 0 QUALIFY key == 0;
SELECT '--';
SELECT (number % 2) AS key, count() FROM numbers(10) GROUP BY key QUALIFY key == 0;
SELECT '--';
SELECT number, COUNT() OVER (PARTITION BY number % 3) AS partition_count FROM numbers(10) QUALIFY COUNT() OVER (PARTITION BY number % 3) = 4 ORDER BY number;
SELECT '--';
EXPLAIN header = 1, actions = 1
SELECT number, COUNT() OVER (PARTITION BY number % 3) AS partition_count FROM numbers(10) QUALIFY COUNT() OVER (PARTITION BY number % 3) = 4 ORDER BY number;
SELECT number % toUInt256(2) AS key, count() FROM numbers(10) GROUP BY key WITH CUBE WITH TOTALS QUALIFY key = toNullable(toNullable(0)); -- { serverError 48 }
SELECT number % 2 AS key, count(materialize(5)) IGNORE NULLS FROM numbers(10) WHERE toLowCardinality(toLowCardinality(materialize(2))) GROUP BY key WITH CUBE WITH TOTALS QUALIFY key = 0; -- { serverError 48 }
SELECT 4, count(4) IGNORE NULLS, number % 2 AS key FROM numbers(10) GROUP BY key WITH ROLLUP WITH TOTALS QUALIFY key = materialize(0); -- { serverError 48 }
SELECT 3, number % toLowCardinality(2) AS key, count() IGNORE NULLS FROM numbers(10) GROUP BY key WITH ROLLUP WITH TOTALS QUALIFY key = 0; -- { serverError 48 }