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
[Minor]: support window functions in order by expressions (#20963)
## Which issue does this PR close?
- Closes#608.
## Rationale for this change
#608 has details but as @alamb describes in
#608 (comment)
```
SELECT c2
FROM test
ORDER BY max(c3) OVER (ORDER BY c9);
```
fails with:
```
This feature is not implemented: Physical plan does not support logical expression WindowFunction(WindowFunction { fun: AggregateUDF(AggregateUDF { inner: Max { signature: Signature { type_signature: UserDefined, volatility: Immutable, parameter_names: None } } }), params: WindowFunctionParams { args: [Column(Column { relation: Some(Bare { table: "test" }), name: "c3" })], partition_by: [], order_by: [Sort { expr: Column(Column { relation: Some(Bare { table: "test" }), name: "c9" }), asc: true, nulls_first: false }], window_frame: WindowFrame { units: Range, start_bound: Preceding(Int32(NULL)), end_bound: CurrentRow, is_causal: false }, filter: None, null_treatment: None, distinct: false } })
```
## What changes are included in this PR?
- relevant change
- slt and unit tests
## Are these changes tested?
Yes added both unit tests for plan and slt tests for checking output
## Are there any user-facing changes?
Additive user changes that users can now use window in order by
expressions
let sql = "SELECT order_id FROM orders ORDER BY MAX(qty) OVER (ORDER BY order_id)";
2811
+
let plan = logical_plan(sql).unwrap();
2812
+
assert_snapshot!(
2813
+
plan,
2814
+
@r"
2815
+
Projection: orders.order_id
2816
+
Sort: max(orders.qty) ORDER BY [orders.order_id ASC NULLS LAST] RANGE BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW ASC NULLS LAST
2817
+
Projection: orders.order_id, max(orders.qty) ORDER BY [orders.order_id ASC NULLS LAST] RANGE BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW
2818
+
WindowAggr: windowExpr=[[max(orders.qty) ORDER BY [orders.order_id ASC NULLS LAST] RANGE BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW]]
2819
+
TableScan: orders
2820
+
"
2821
+
);
2822
+
}
2823
+
2824
+
#[test]
2825
+
fnwindow_function_in_select_and_order_by(){
2826
+
let sql = "SELECT order_id, MAX(qty) OVER (ORDER BY order_id) FROM orders ORDER BY MAX(qty) OVER (ORDER BY order_id)";
2827
+
let plan = logical_plan(sql).unwrap();
2828
+
assert_snapshot!(
2829
+
plan,
2830
+
@r"
2831
+
Sort: max(orders.qty) ORDER BY [orders.order_id ASC NULLS LAST] RANGE BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW ASC NULLS LAST
2832
+
Projection: orders.order_id, max(orders.qty) ORDER BY [orders.order_id ASC NULLS LAST] RANGE BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW
2833
+
WindowAggr: windowExpr=[[max(orders.qty) ORDER BY [orders.order_id ASC NULLS LAST] RANGE BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW]]
2834
+
TableScan: orders
2835
+
"
2836
+
);
2837
+
}
2838
+
2839
+
#[test]
2840
+
fnwindow_function_in_order_by_nested_expr(){
2841
+
let sql =
2842
+
"SELECT order_id FROM orders ORDER BY MAX(qty) OVER (ORDER BY order_id) + 1";
2843
+
let plan = logical_plan(sql).unwrap();
2844
+
assert_snapshot!(
2845
+
plan,
2846
+
@r"
2847
+
Projection: orders.order_id
2848
+
Sort: max(orders.qty) ORDER BY [orders.order_id ASC NULLS LAST] RANGE BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW + Int64(1) ASC NULLS LAST
2849
+
Projection: orders.order_id, max(orders.qty) ORDER BY [orders.order_id ASC NULLS LAST] RANGE BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW
2850
+
WindowAggr: windowExpr=[[max(orders.qty) ORDER BY [orders.order_id ASC NULLS LAST] RANGE BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW]]
2851
+
TableScan: orders
2852
+
"
2853
+
);
2854
+
}
2855
+
2856
+
#[test]
2857
+
fnwindow_function_in_order_by_desc(){
2858
+
let sql =
2859
+
"SELECT order_id FROM orders ORDER BY MAX(qty) OVER (ORDER BY order_id) DESC";
2860
+
let plan = logical_plan(sql).unwrap();
2861
+
assert_snapshot!(
2862
+
plan,
2863
+
@r"
2864
+
Projection: orders.order_id
2865
+
Sort: max(orders.qty) ORDER BY [orders.order_id ASC NULLS LAST] RANGE BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW DESC NULLS FIRST
2866
+
Projection: orders.order_id, max(orders.qty) ORDER BY [orders.order_id ASC NULLS LAST] RANGE BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW
2867
+
WindowAggr: windowExpr=[[max(orders.qty) ORDER BY [orders.order_id ASC NULLS LAST] RANGE BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW]]
2868
+
TableScan: orders
2869
+
"
2870
+
);
2871
+
}
2872
+
2873
+
#[test]
2874
+
fnmultiple_window_functions_in_order_by(){
2875
+
let sql = "SELECT order_id FROM orders ORDER BY MAX(qty) OVER (ORDER BY order_id), MIN(qty) OVER (ORDER BY order_id DESC)";
2876
+
let plan = logical_plan(sql).unwrap();
2877
+
assert_snapshot!(
2878
+
plan,
2879
+
@r"
2880
+
Projection: orders.order_id
2881
+
Sort: max(orders.qty) ORDER BY [orders.order_id ASC NULLS LAST] RANGE BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW ASC NULLS LAST, min(orders.qty) ORDER BY [orders.order_id DESC NULLS FIRST] RANGE BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW ASC NULLS LAST
2882
+
Projection: orders.order_id, max(orders.qty) ORDER BY [orders.order_id ASC NULLS LAST] RANGE BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW, min(orders.qty) ORDER BY [orders.order_id DESC NULLS FIRST] RANGE BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW
2883
+
WindowAggr: windowExpr=[[max(orders.qty) ORDER BY [orders.order_id ASC NULLS LAST] RANGE BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW]]
2884
+
WindowAggr: windowExpr=[[min(orders.qty) ORDER BY [orders.order_id DESC NULLS FIRST] RANGE BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW]]
2885
+
TableScan: orders
2886
+
"
2887
+
);
2888
+
}
2889
+
2890
+
#[test]
2891
+
fnwindow_function_in_order_by_with_group_by(){
2892
+
let sql = "SELECT order_id, SUM(qty) FROM orders GROUP BY order_id ORDER BY MAX(SUM(qty)) OVER (ORDER BY order_id)";
2893
+
let plan = logical_plan(sql).unwrap();
2894
+
assert_snapshot!(
2895
+
plan,
2896
+
@r"
2897
+
Projection: orders.order_id, sum(orders.qty)
2898
+
Sort: max(sum(orders.qty)) ORDER BY [orders.order_id ASC NULLS LAST] RANGE BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW ASC NULLS LAST
2899
+
Projection: orders.order_id, sum(orders.qty), max(sum(orders.qty)) ORDER BY [orders.order_id ASC NULLS LAST] RANGE BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW
2900
+
WindowAggr: windowExpr=[[max(sum(orders.qty)) ORDER BY [orders.order_id ASC NULLS LAST] RANGE BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW]]
let sql = "SELECT person.id, ROW_NUMBER() OVER (PARTITION BY person.age ORDER BY person.id) as rn FROM person QUALIFY rn = 1 ORDER BY ROW_NUMBER() OVER (PARTITION BY person.age ORDER BY person.id)";
2910
+
let plan = logical_plan(sql).unwrap();
2911
+
assert_snapshot!(
2912
+
plan,
2913
+
@r"
2914
+
Sort: rn ASC NULLS LAST
2915
+
Projection: person.id, row_number() PARTITION BY [person.age] ORDER BY [person.id ASC NULLS LAST] RANGE BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW AS rn
2916
+
Filter: row_number() PARTITION BY [person.age] ORDER BY [person.id ASC NULLS LAST] RANGE BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW = Int64(1)
2917
+
WindowAggr: windowExpr=[[row_number() PARTITION BY [person.age] ORDER BY [person.id ASC NULLS LAST] RANGE BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW]]
2918
+
TableScan: person
2919
+
"
2920
+
);
2921
+
}
2922
+
2923
+
#[test]
2924
+
fnwindow_function_in_order_by_not_in_select(){
2925
+
let sql =
2926
+
"SELECT order_id FROM orders ORDER BY MIN(qty) OVER (PARTITION BY order_id)";
2927
+
let plan = logical_plan(sql).unwrap();
2928
+
assert_snapshot!(
2929
+
plan,
2930
+
@r"
2931
+
Projection: orders.order_id
2932
+
Sort: min(orders.qty) PARTITION BY [orders.order_id] ROWS BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING ASC NULLS LAST
2933
+
Projection: orders.order_id, min(orders.qty) PARTITION BY [orders.order_id] ROWS BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING
2934
+
WindowAggr: windowExpr=[[min(orders.qty) PARTITION BY [orders.order_id] ROWS BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING]]
2935
+
TableScan: orders
2936
+
"
2937
+
);
2938
+
}
2939
+
2808
2940
#[test]
2809
2941
fnover_order_by_with_window_frame_single_end(){
2810
2942
let sql = "SELECT order_id, MAX(qty) OVER (ORDER BY order_id ROWS 3 PRECEDING), MIN(qty) OVER (ORDER BY order_id DESC) from orders";
let sql = "SELECT person.id FROM person QUALIFY person.id > 1 ORDER BY ROW_NUMBER() OVER (ORDER BY person.id)";
4394
+
let err = logical_plan(sql).unwrap_err();
4395
+
assert_eq!(
4396
+
err.strip_backtrace(),
4397
+
"Error during planning: QUALIFY clause requires window functions in the SELECT list or QUALIFY clause"
4398
+
);
4399
+
}
4400
+
4259
4401
#[test]
4260
4402
fntest_select_qualify_complex_condition(){
4261
4403
let sql = "SELECT person.id, person.age, ROW_NUMBER() OVER (PARTITION BY person.age ORDER BY person.id) as rn, RANK() OVER (ORDER BY person.salary) as rank FROM person QUALIFY rn <= 2 AND rank <= 5";
0 commit comments