You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
// This step "dereferences" any aliases in the QUALIFY clause.
204
+
//
205
+
// This is how we support queries with QUALIFY expressions that
206
+
// refer to aliased columns.
207
+
//
208
+
// For example:
209
+
//
210
+
// select row_number() over (PARTITION BY id) as rk from users qualify rk > 1;
211
+
//
212
+
// are rewritten as, respectively:
213
+
//
214
+
// select row_number() over (PARTITION BY id) as rk from users qualify row_number() over (PARTITION BY id) > 1;
215
+
//
216
+
let qualify_expr = resolve_aliases_to_exprs(qualify_expr,&alias_map)?;
217
+
normalize_col(qualify_expr,&projected_plan)
218
+
})
219
+
.transpose()?;
220
+
221
+
// The outer expressions we will search through for aggregates.
222
+
// Aggregates may be sourced from the SELECT list or from the HAVING expression.
223
+
let aggr_expr_haystack = select_exprs
224
+
.iter()
225
+
.chain(having_expr_opt.iter())
226
+
.chain(qualify_expr_opt.iter());
227
+
// All of the aggregate expressions (deduplicated).
228
+
let aggr_exprs = find_aggregate_exprs(aggr_expr_haystack);
229
+
226
230
// Process group by, aggregation or having
227
-
let(plan,mut select_exprs_post_aggr, having_expr_post_aggr) = if !group_by_exprs
228
-
.is_empty()
229
-
|| !aggr_exprs.is_empty()
230
-
{
231
+
let(
232
+
plan,
233
+
mut select_exprs_post_aggr,
234
+
having_expr_post_aggr,
235
+
qualify_expr_post_aggr,
236
+
) = if !group_by_exprs.is_empty() || !aggr_exprs.is_empty(){
231
237
self.aggregate(
232
238
&base_plan,
233
239
&select_exprs,
234
240
having_expr_opt.as_ref(),
241
+
qualify_expr_opt.as_ref(),
235
242
&group_by_exprs,
236
243
&aggr_exprs,
237
244
)?
238
245
}else{
239
246
match having_expr_opt {
240
247
Some(having_expr) => returnplan_err!("HAVING clause references: {having_expr} must appear in the GROUP BY clause or be used in an aggregate function"),
.with_help(format!("Either add '{expr}' to GROUP BY clause, or use an aggregare function like ANY_VALUE({expr})"),None);
174
+
.with_help(format!("Either add '{expr}' to GROUP BY clause, or use an aggregate function like ANY_VALUE({expr})"),None);
166
175
167
176
returnplan_err!(
168
177
"{}: While expanding wildcard, column \"{}\" must appear in the GROUP BY clause or must be part of an aggregate function, currently only \"{}\" appears in the SELECT clause satisfies this requirement",
assert_snapshot!(diag.message, @"'person.first_name' must appear in GROUP BY clause because it's not an aggregate expression");
186
186
assert_eq!(diag.span,Some(spans["a"]));
187
-
assert_snapshot!(diag.helps[0].message, @"Either add 'person.first_name' to GROUP BY clause, or use an aggregare function like ANY_VALUE(person.first_name)");
187
+
assert_snapshot!(diag.helps[0].message, @"Either add 'person.first_name' to GROUP BY clause, or use an aggregate function like ANY_VALUE(person.first_name)");
Copy file name to clipboardExpand all lines: datafusion/sql/tests/sql_integration.rs
+61Lines changed: 61 additions & 0 deletions
Original file line number
Diff line number
Diff line change
@@ -4202,6 +4202,67 @@ Projection: person.id, row_number() PARTITION BY [person.age] ORDER BY [person.i
4202
4202
);
4203
4203
}
4204
4204
4205
+
#[test]
4206
+
fntest_select_qualify_aggregate_reference(){
4207
+
let sql = "
4208
+
SELECT
4209
+
person.id,
4210
+
ROW_NUMBER() OVER (PARTITION BY person.id ORDER BY person.id) as rn
4211
+
FROM person
4212
+
GROUP BY
4213
+
person.id
4214
+
QUALIFY rn = 1 AND SUM(person.age) > 0";
4215
+
let plan = logical_plan(sql).unwrap();
4216
+
assert_snapshot!(
4217
+
plan,
4218
+
@r"
4219
+
Projection: person.id, row_number() PARTITION BY [person.id] ORDER BY [person.id ASC NULLS LAST] RANGE BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW AS rn
4220
+
Filter: row_number() PARTITION BY [person.id] ORDER BY [person.id ASC NULLS LAST] RANGE BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW = Int64(1) AND sum(person.age) > Int64(0)
4221
+
WindowAggr: windowExpr=[[row_number() PARTITION BY [person.id] ORDER BY [person.id ASC NULLS LAST] RANGE BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW]]
QUALIFY ROW_NUMBER() OVER (PARTITION BY person.id ORDER BY SUM(person.age) DESC) = 1";
4237
+
let plan = logical_plan(sql).unwrap();
4238
+
assert_snapshot!(
4239
+
plan,
4240
+
@r"
4241
+
Projection: person.id
4242
+
Filter: row_number() PARTITION BY [person.id] ORDER BY [sum(person.age) DESC NULLS FIRST] RANGE BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW = Int64(1)
4243
+
WindowAggr: windowExpr=[[row_number() PARTITION BY [person.id] ORDER BY [sum(person.age) DESC NULLS FIRST] RANGE BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW]]
QUALIFY ROW_NUMBER() OVER (PARTITION BY person.id ORDER BY person.age DESC) = 1";
4259
+
let err = logical_plan(sql).unwrap_err();
4260
+
assert_snapshot!(
4261
+
err.strip_backtrace(),
4262
+
@r#"Error during planning: Column in QUALIFY must be in GROUP BY or an aggregate function: While expanding wildcard, column "person.age" must appear in the GROUP BY clause or must be part of an aggregate function, currently only "person.id" appears in the SELECT clause satisfies this requirement"#
4263
+
);
4264
+
}
4265
+
4205
4266
#[test]
4206
4267
fntest_select_qualify_without_window_function(){
4207
4268
let sql = "SELECT person.id FROM person QUALIFY person.id > 1";
0 commit comments