Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
pola-rs
GitHub Repository: pola-rs/polars
Path: blob/main/crates/polars-sql/tests/functions_windows.rs
7884 views
1
use polars_core::prelude::*;
2
use polars_lazy::prelude::*;
3
use polars_plan::dsl::Expr;
4
use polars_sql::*;
5
6
fn create_df() -> LazyFrame {
7
df! {
8
"a" => [1, 1, 1, 2, 2, 3],
9
"b" => ["a", "b", "c", "a", "b", "c"]
10
}
11
.unwrap()
12
.lazy()
13
}
14
15
fn create_expected(exprs: &[Expr], sql: &str) -> (DataFrame, DataFrame) {
16
let df = create_df();
17
18
let query = format!(
19
r#"
20
SELECT
21
{sql}
22
FROM
23
df
24
"#
25
);
26
27
let expected = df.clone().select(exprs).collect().unwrap();
28
let mut ctx = SQLContext::new();
29
ctx.register("df", df);
30
31
let actual = ctx.execute(&query).unwrap().collect().unwrap();
32
(expected, actual)
33
}
34
35
fn ensure_error(sql: &str, expected_error: &str) {
36
let df = create_df();
37
let query = format!(
38
r#"
39
SELECT
40
{sql}
41
FROM
42
df
43
"#
44
);
45
46
let mut ctx = SQLContext::new();
47
ctx.register("df", df);
48
match ctx.execute(&query) {
49
Ok(_) => panic!("expected error: {expected_error}"),
50
Err(e) => {
51
assert!(
52
e.to_string().contains(expected_error),
53
"expected error: {expected_error}, got: {e}",
54
)
55
},
56
};
57
}
58
59
#[test]
60
fn test_lead_lag() {
61
for shift in [-2, -1, 1, 2] {
62
let (sql_func, sql_shift) = if shift > 0 {
63
("LAG", shift)
64
} else {
65
("LEAD", -shift)
66
};
67
let exprs = [
68
col("a"),
69
col("b"),
70
col("b")
71
.shift(shift.into())
72
.over_with_options(
73
Some([col("a")]),
74
Some(([col("b")], SortOptions::new().with_order_descending(false))),
75
Default::default(),
76
)
77
.unwrap()
78
.alias("c"),
79
];
80
81
let sql_expr =
82
format!("a, b, {sql_func}(b, {sql_shift}) OVER (PARTITION BY a ORDER BY b) as c");
83
let (expected, actual) = create_expected(&exprs, &sql_expr);
84
85
assert_eq!(expected, actual, "shift: {shift}");
86
}
87
}
88
89
#[test]
90
fn test_lead_lag_default() {
91
for shift in [-1, 1] {
92
let sql_func = if shift > 0 { "LAG" } else { "LEAD" };
93
let exprs = [
94
col("a"),
95
col("b"),
96
col("b")
97
.shift(shift.into())
98
.over_with_options(
99
Some([col("a")]),
100
Some(([col("b")], SortOptions::new().with_order_descending(false))),
101
Default::default(),
102
)
103
.unwrap()
104
.alias("c"),
105
];
106
107
let sql_expr = format!("a, b, {sql_func}(b) OVER (PARTITION BY a ORDER BY b) as c");
108
let (expected, actual) = create_expected(&exprs, &sql_expr);
109
110
assert_eq!(expected, actual, "shift: {shift}");
111
}
112
}
113
114
#[test]
115
fn test_incorrect_shift() {
116
for func in ["LAG", "LEAD"] {
117
// Type of second argument is not an integer
118
ensure_error(
119
&format!("a, b, {func}(b, '1') OVER (PARTITION BY a ORDER BY b) as c"),
120
"offset must be an integer",
121
);
122
ensure_error(
123
&format!("a, b, {func}(b, 1.0) OVER (PARTITION BY a ORDER BY b) as c"),
124
"offset must be an integer",
125
);
126
ensure_error(
127
&format!("a, b, {func}(b, 1.0) OVER (PARTITION BY a ORDER BY b) as c"),
128
"offset must be an integer",
129
);
130
131
// Number of arguments is incorrect
132
ensure_error(
133
&format!("a, b, {func}() OVER (PARTITION BY a ORDER BY b) as c"),
134
"expects 1 or 2 arguments",
135
);
136
ensure_error(
137
&format!("a, b, {func}(b, 1, 2) OVER (PARTITION BY a ORDER BY b) as c"),
138
"expects 1 or 2 arguments",
139
);
140
141
// Second argument is not a constant
142
ensure_error(
143
&format!("a, b, {func}(b, a) OVER (PARTITION BY a ORDER BY b) as c"),
144
"offset must be an integer",
145
);
146
ensure_error(
147
&format!("a, b, {func}(b, a + 1) OVER (PARTITION BY a ORDER BY b) as c"),
148
"offset must be an integer",
149
);
150
151
// Second argument is not positive
152
ensure_error(
153
&format!("a, b, {func}(b, -1) OVER (PARTITION BY a ORDER BY b) as c"),
154
"offset must be positive",
155
);
156
ensure_error(
157
&format!("a, b, {func}(b, 0) OVER (PARTITION BY a ORDER BY b) as c"),
158
"offset must be positive",
159
);
160
}
161
}
162
163
#[test]
164
fn test_lead_lag_without_partition_by() {
165
// Test LAG/LEAD with ORDER BY but without PARTITION BY
166
// This should work correctly over the entire dataset
167
for shift in [-1, 1] {
168
let (sql_func, shift_value) = if shift > 0 {
169
("LAG", shift)
170
} else {
171
("LEAD", -shift)
172
};
173
174
let exprs = [
175
col("a"),
176
col("b"),
177
col("a")
178
.shift(shift.into())
179
.over_with_options(
180
None, // No partition by
181
Some(([col("a")], SortOptions::new().with_order_descending(false))),
182
Default::default(),
183
)
184
.unwrap()
185
.alias("a_shifted"),
186
];
187
188
let sql_expr = format!("a, b, {sql_func}(a, {shift_value}) OVER (ORDER BY a) as a_shifted");
189
let (expected, actual) = create_expected(&exprs, &sql_expr);
190
191
assert_eq!(expected, actual, "shift: {shift}");
192
}
193
}
194
195
#[test]
196
fn test_lead_lag_without_over_clause() {
197
// LAG/LEAD without OVER clause should raise an error
198
for func in ["LAG", "LEAD"] {
199
ensure_error(&format!("a, b, {func}(b) as c"), "requires an OVER clause");
200
ensure_error(
201
&format!("a, b, {func}(b, 1) as c"),
202
"requires an OVER clause",
203
);
204
}
205
}
206
207
#[test]
208
fn test_lead_lag_without_order_by() {
209
// LAG/LEAD with OVER clause but without ORDER BY should raise an error
210
for func in ["LAG", "LEAD"] {
211
ensure_error(
212
&format!("a, b, {func}(b) OVER (PARTITION BY a) as c"),
213
"requires an ORDER BY",
214
);
215
ensure_error(
216
&format!("a, b, {func}(b, 1) OVER (PARTITION BY a) as c"),
217
"requires an ORDER BY",
218
);
219
// OVER clause with empty parentheses
220
ensure_error(
221
&format!("a, b, {func}(b) OVER () as c"),
222
"requires an ORDER BY",
223
);
224
}
225
}
226
227