1use nodedb_types::Value;
16
17use super::arg::{ArgValues, arg_at, eval_arg_values};
18use super::extremum::value_replaces;
19use super::frame::{build_peer_groups, evaluate_frame_bounds};
20use super::helpers::{as_f64, set_window_col};
21use super::running::running_aggregate;
22use super::spec::{FrameBound, WindowFuncSpec};
23use crate::numeric_sum::{ExactSum, sum_input};
24
25pub(super) fn apply_aggregate_window(
26 rows: &mut [(String, Value)],
27 indices: &[usize],
28 spec: &WindowFuncSpec,
29) -> Result<(), crate::expr::EvalError> {
30 let arg_values = eval_arg_values(rows, indices, spec, 0)?;
34
35 let use_running = spec.frame.mode == "range"
39 && matches!(spec.frame.start, FrameBound::UnboundedPreceding)
40 && matches!(spec.frame.end, FrameBound::CurrentRow);
41
42 if use_running {
43 running_aggregate(rows, indices, spec, &arg_values)?;
44 return Ok(());
45 }
46
47 per_row_aggregate(rows, indices, spec, &arg_values)
48}
49
50fn per_row_aggregate(
58 rows: &mut [(String, Value)],
59 indices: &[usize],
60 spec: &WindowFuncSpec,
61 arg_values: &ArgValues,
62) -> Result<(), crate::expr::EvalError> {
63 let len = indices.len();
64 if len == 0 {
65 return Ok(());
66 }
67
68 let order_expr = spec.order_by.first().map(|(expr, _)| expr);
70 let order_values: Vec<Value> = indices
71 .iter()
72 .map(|&i| match order_expr {
73 Some(expr) => expr.eval(&rows[i].1),
74 None => Ok(Value::Null),
75 })
76 .collect::<Result<Vec<_>, _>>()?;
77
78 let peer_groups: Vec<usize> = if spec.frame.mode == "groups" {
81 build_peer_groups(&order_values)
82 } else {
83 Vec::new()
84 };
85
86 let all_vals: Vec<Option<f64>> = (0..len)
88 .map(|pos| as_f64(&arg_at(arg_values, pos)))
89 .collect();
90
91 let results: Vec<Value> = (0..len)
92 .map(|pos| {
93 let (start_idx, end_idx) =
94 evaluate_frame_bounds(&spec.frame, pos, len, &order_values, &peer_groups);
95
96 aggregate_slice(&all_vals, arg_values, spec, start_idx, end_idx)
97 })
98 .collect::<Result<_, _>>()?;
99
100 for (pos, result) in results.into_iter().enumerate() {
101 let row_idx = indices[pos];
102 set_window_col(&mut rows[row_idx].1, &spec.alias, result);
103 }
104 Ok(())
105}
106
107fn frame_sum(arg_values: &ArgValues, start_idx: usize, end_idx: usize) -> ExactSum {
109 let mut acc = ExactSum::new();
110 if let Some(values) = arg_values {
111 for v in values.get(start_idx..=end_idx).unwrap_or(&[]) {
112 if let Some(n) = sum_input(v) {
113 acc.add_value(&n);
114 }
115 }
116 }
117 acc
118}
119
120fn aggregate_slice(
123 all_vals: &[Option<f64>],
124 arg_values: &ArgValues,
125 spec: &WindowFuncSpec,
126 start_idx: usize,
127 end_idx: usize,
128) -> Result<Value, crate::expr::EvalError> {
129 Ok(match spec.func_name.as_str() {
130 "sum" => frame_sum(arg_values, start_idx, end_idx).sum()?,
131 "count" => match arg_values {
135 None => Value::from_u64((end_idx - start_idx + 1) as u64),
136 Some(values) => Value::from_u64(
137 values[start_idx..=end_idx]
138 .iter()
139 .filter(|v| !v.is_null())
140 .count() as u64,
141 ),
142 },
143 "avg" => frame_sum(arg_values, start_idx, end_idx).avg()?,
144 "min" | "max" => {
145 let want_max = spec.func_name == "max";
146 let Some(values) = arg_values else {
147 return Ok(Value::Null);
148 };
149 let frame_values = values.get(start_idx..=end_idx).unwrap_or(&[]);
150 let mut best: Option<&Value> = None;
151 for (numeric, candidate) in all_vals[start_idx..=end_idx].iter().zip(frame_values) {
152 if numeric.is_none() {
153 continue;
154 }
155 if value_replaces(candidate, best, want_max) {
156 best = Some(candidate);
157 }
158 }
159 best.cloned().unwrap_or(Value::Null)
160 }
161 "first_value" => arg_at(arg_values, start_idx),
162 "last_value" => arg_at(arg_values, end_idx),
163 _ => Value::Null,
164 })
165}
166
167#[cfg(test)]
168mod tests {
169 use super::super::spec::{FrameBound, WindowFrame, WindowFuncSpec};
170 use super::apply_aggregate_window;
171 use crate::expr::SqlExpr;
172 use nodedb_types::Value;
173 use serde_json::json;
174
175 fn v(j: serde_json::Value) -> Value {
176 Value::from(j)
177 }
178
179 fn res(row: &(String, Value)) -> Value {
180 row.1.get("result").cloned().unwrap_or(Value::Null)
181 }
182
183 fn numbered(n: usize) -> Vec<(String, Value)> {
184 (1..=n)
185 .map(|i| (i.to_string(), v(json!({ "n": i as i64 }))))
186 .collect()
187 }
188
189 fn make_spec(func: &str, field: &str, frame: WindowFrame) -> WindowFuncSpec {
190 WindowFuncSpec {
191 alias: "result".into(),
192 func_name: func.into(),
193 args: if field == "*" {
194 vec![]
195 } else {
196 vec![SqlExpr::Column(field.into())]
197 },
198 partition_by: vec![],
199 order_by: vec![(SqlExpr::Column("n".into()), true)],
200 frame,
201 }
202 }
203
204 fn rows_frame(start: FrameBound, end: FrameBound) -> WindowFrame {
205 WindowFrame {
206 mode: "rows".into(),
207 start,
208 end,
209 }
210 }
211
212 fn range_frame(start: FrameBound, end: FrameBound) -> WindowFrame {
213 WindowFrame {
214 mode: "range".into(),
215 start,
216 end,
217 }
218 }
219
220 fn groups_frame(start: FrameBound, end: FrameBound) -> WindowFrame {
221 WindowFrame {
222 mode: "groups".into(),
223 start,
224 end,
225 }
226 }
227
228 #[test]
231 fn rows_1_preceding_1_following_sum() {
232 let mut rows = numbered(5);
233 let indices: Vec<usize> = (0..5).collect();
234 let spec = make_spec(
235 "sum",
236 "n",
237 rows_frame(FrameBound::Preceding(1), FrameBound::Following(1)),
238 );
239 apply_aggregate_window(&mut rows, &indices, &spec).unwrap();
240 assert_eq!(res(&rows[0]), Value::Integer(3));
246 assert_eq!(res(&rows[1]), Value::Integer(6));
247 assert_eq!(res(&rows[2]), Value::Integer(9));
248 assert_eq!(res(&rows[3]), Value::Integer(12));
249 assert_eq!(res(&rows[4]), Value::Integer(9));
250 }
251
252 #[test]
253 fn rows_unbounded_preceding_current_sum() {
254 let mut rows = numbered(5);
255 let indices: Vec<usize> = (0..5).collect();
256 let spec = make_spec(
257 "sum",
258 "n",
259 rows_frame(FrameBound::UnboundedPreceding, FrameBound::CurrentRow),
260 );
261 apply_aggregate_window(&mut rows, &indices, &spec).unwrap();
262 assert_eq!(res(&rows[0]), Value::Integer(1));
263 assert_eq!(res(&rows[1]), Value::Integer(3));
264 assert_eq!(res(&rows[2]), Value::Integer(6));
265 assert_eq!(res(&rows[3]), Value::Integer(10));
266 assert_eq!(res(&rows[4]), Value::Integer(15));
267 }
268
269 #[test]
270 fn rows_current_unbounded_following_sum() {
271 let mut rows = numbered(5);
272 let indices: Vec<usize> = (0..5).collect();
273 let spec = make_spec(
274 "sum",
275 "n",
276 rows_frame(FrameBound::CurrentRow, FrameBound::UnboundedFollowing),
277 );
278 apply_aggregate_window(&mut rows, &indices, &spec).unwrap();
279 assert_eq!(res(&rows[0]), Value::Integer(15));
283 assert_eq!(res(&rows[1]), Value::Integer(14));
284 assert_eq!(res(&rows[4]), Value::Integer(5));
285 }
286
287 #[test]
290 fn range_unbounded_preceding_current_row_with_ties() {
291 let mut rows = vec![
293 ("a".into(), v(json!({"n": 1i64}))),
294 ("b".into(), v(json!({"n": 1i64}))),
295 ("c".into(), v(json!({"n": 2i64}))),
296 ("d".into(), v(json!({"n": 3i64}))),
297 ];
298 let indices: Vec<usize> = (0..4).collect();
299 let spec = make_spec(
303 "sum",
304 "n",
305 range_frame(FrameBound::UnboundedPreceding, FrameBound::CurrentRow),
306 );
307 apply_aggregate_window(&mut rows, &indices, &spec).unwrap();
308 assert_eq!(res(&rows[0]), Value::Integer(2));
310 assert_eq!(res(&rows[1]), Value::Integer(2));
312 assert_eq!(res(&rows[2]), Value::Integer(4));
314 assert_eq!(res(&rows[3]), Value::Integer(7));
316 }
317
318 #[test]
321 fn frame_and_running_sum_keep_integers_above_2_pow_53_exact() {
322 let vals = [
323 Value::Integer(9_007_199_254_740_993),
324 Value::Integer(9_007_199_254_740_992),
325 ];
326 let indices: Vec<usize> = (0..2).collect();
327
328 let mut rows = keyed(&vals);
329 apply_aggregate_window(&mut rows, &indices, &make_spec("sum", "v", whole())).unwrap();
330 assert_eq!(res(&rows[0]), Value::Integer(18_014_398_509_481_985));
331
332 let running = range_frame(FrameBound::UnboundedPreceding, FrameBound::CurrentRow);
333 let mut rows = keyed(&vals);
334 apply_aggregate_window(&mut rows, &indices, &make_spec("sum", "v", running)).unwrap();
335 assert_eq!(
336 results(&rows),
337 vec![
338 Value::Integer(9_007_199_254_740_993),
339 Value::Integer(18_014_398_509_481_985)
340 ]
341 );
342
343 let mut rows = keyed(&vals);
344 apply_aggregate_window(&mut rows, &indices, &make_spec("avg", "v", whole())).unwrap();
345 assert_eq!(res(&rows[0]), Value::Float(9_007_199_254_740_992.0));
346 }
347
348 #[test]
349 fn frame_sum_past_i64_and_u64_is_exact() {
350 let vals = [
351 Value::Integer(i64::MAX),
352 Value::from_u64(u64::MAX),
353 Value::from_u64(u64::MAX),
354 ];
355 let indices: Vec<usize> = (0..3).collect();
356 let mut rows = keyed(&vals);
357 apply_aggregate_window(&mut rows, &indices, &make_spec("sum", "v", whole())).unwrap();
358 let want = i128::from(i64::MAX) + 2 * i128::from(u64::MAX);
359 assert_eq!(
360 res(&rows[0]),
361 Value::Decimal(rust_decimal::Decimal::from_i128_with_scale(want, 0))
362 );
363 }
364
365 #[test]
366 fn frame_sum_mixed_int_float_is_float_and_empty_is_null() {
367 let vals = [Value::Integer(2), Value::Float(0.5), Value::Null];
368 let indices: Vec<usize> = (0..3).collect();
369 let mut rows = keyed(&vals);
370 apply_aggregate_window(&mut rows, &indices, &make_spec("sum", "v", whole())).unwrap();
371 assert_eq!(res(&rows[0]), Value::Float(2.5));
372
373 let one_row = rows_frame(FrameBound::CurrentRow, FrameBound::CurrentRow);
374 let mut rows = keyed(&vals);
375 apply_aggregate_window(&mut rows, &indices, &make_spec("sum", "v", one_row)).unwrap();
376 assert_eq!(res(&rows[2]), Value::Null);
377 }
378
379 #[test]
382 fn frame_and_running_sum_keep_non_finite_floats() {
383 let indices: Vec<usize> = (0..2).collect();
384 let overflow = [Value::Float(1e308), Value::Float(1e308)];
385
386 let mut rows = keyed(&overflow);
387 apply_aggregate_window(&mut rows, &indices, &make_spec("sum", "v", whole())).unwrap();
388 assert_eq!(res(&rows[0]), Value::Float(f64::INFINITY));
389
390 let running = range_frame(FrameBound::UnboundedPreceding, FrameBound::CurrentRow);
391 let mut rows = keyed(&overflow);
392 apply_aggregate_window(&mut rows, &indices, &make_spec("sum", "v", running)).unwrap();
393 assert_eq!(
394 results(&rows),
395 vec![Value::Float(1e308), Value::Float(f64::INFINITY)]
396 );
397
398 let cancel = [Value::Float(f64::INFINITY), Value::Float(f64::NEG_INFINITY)];
399 let mut rows = keyed(&cancel);
400 apply_aggregate_window(&mut rows, &indices, &make_spec("avg", "v", whole())).unwrap();
401 assert!(matches!(res(&rows[0]), Value::Float(f) if f.is_nan()));
402 }
403
404 fn keyed(vals: &[Value]) -> Vec<(String, Value)> {
408 vals.iter()
409 .enumerate()
410 .map(|(i, val)| {
411 let doc = std::collections::HashMap::from([
412 ("n".to_string(), Value::Integer(i as i64 + 1)),
413 ("v".to_string(), val.clone()),
414 ]);
415 (i.to_string(), Value::Object(doc))
416 })
417 .collect()
418 }
419
420 fn results(rows: &[(String, Value)]) -> Vec<Value> {
421 rows.iter().map(res).collect()
422 }
423
424 fn whole() -> WindowFrame {
425 rows_frame(
426 FrameBound::UnboundedPreceding,
427 FrameBound::UnboundedFollowing,
428 )
429 }
430
431 #[test]
432 fn frame_min_max_keep_integers_above_2_pow_53_exact() {
433 let vals = [
434 Value::Integer(9_007_199_254_740_993),
435 Value::Integer(9_007_199_254_740_992),
436 ];
437 let indices: Vec<usize> = (0..2).collect();
438
439 let mut rows = keyed(&vals);
440 apply_aggregate_window(&mut rows, &indices, &make_spec("min", "v", whole())).unwrap();
441 assert_eq!(res(&rows[0]), Value::Integer(9_007_199_254_740_992));
442
443 let mut rows = keyed(&vals);
444 apply_aggregate_window(&mut rows, &indices, &make_spec("max", "v", whole())).unwrap();
445 assert_eq!(res(&rows[0]), Value::Integer(9_007_199_254_740_993));
446 }
447
448 #[test]
449 fn frame_min_max_over_nanosecond_timestamps() {
450 let vals = [
451 Value::Integer(1_700_000_000_000_000_002),
452 Value::Integer(1_700_000_000_000_000_001),
453 Value::Integer(1_700_000_000_000_000_003),
454 ];
455 let indices: Vec<usize> = (0..3).collect();
456
457 let mut rows = keyed(&vals);
458 apply_aggregate_window(&mut rows, &indices, &make_spec("min", "v", whole())).unwrap();
459 assert_eq!(res(&rows[2]), Value::Integer(1_700_000_000_000_000_001));
460
461 let mut rows = keyed(&vals);
462 apply_aggregate_window(&mut rows, &indices, &make_spec("max", "v", whole())).unwrap();
463 assert_eq!(res(&rows[2]), Value::Integer(1_700_000_000_000_000_003));
464 }
465
466 #[test]
467 fn frame_min_max_mixed_int_float_return_original_type() {
468 let vals = [
469 Value::Integer(9_007_199_254_740_993),
470 Value::Float(9_007_199_254_740_992.0),
471 Value::Float(1.5),
472 Value::Integer(2),
473 ];
474 let indices: Vec<usize> = (0..4).collect();
475
476 let mut rows = keyed(&vals);
477 apply_aggregate_window(&mut rows, &indices, &make_spec("min", "v", whole())).unwrap();
478 assert_eq!(res(&rows[0]), Value::Float(1.5));
479
480 let mut rows = keyed(&vals);
481 apply_aggregate_window(&mut rows, &indices, &make_spec("max", "v", whole())).unwrap();
482 assert_eq!(res(&rows[0]), Value::Integer(9_007_199_254_740_993));
483 }
484
485 #[test]
488 fn frame_min_max_place_nan_above_every_number() {
489 let vals = [Value::Float(f64::NAN), Value::Integer(3), Value::Float(1.5)];
490 let indices: Vec<usize> = (0..3).collect();
491
492 let mut rows = keyed(&vals);
493 apply_aggregate_window(&mut rows, &indices, &make_spec("max", "v", whole())).unwrap();
494 assert!(matches!(res(&rows[0]), Value::Float(f) if f.is_nan()));
495
496 let mut rows = keyed(&vals);
497 apply_aggregate_window(&mut rows, &indices, &make_spec("min", "v", whole())).unwrap();
498 assert_eq!(res(&rows[0]), Value::Float(1.5));
499 }
500
501 #[test]
502 fn running_min_max_keep_integers_exact() {
503 let vals = [
504 Value::Integer(9_007_199_254_740_993),
505 Value::Integer(9_007_199_254_740_992),
506 Value::Integer(9_007_199_254_740_995),
507 ];
508 let indices: Vec<usize> = (0..3).collect();
509 let running = || range_frame(FrameBound::UnboundedPreceding, FrameBound::CurrentRow);
510
511 let mut rows = keyed(&vals);
512 apply_aggregate_window(&mut rows, &indices, &make_spec("min", "v", running())).unwrap();
513 assert_eq!(
514 results(&rows),
515 vec![
516 Value::Integer(9_007_199_254_740_993),
517 Value::Integer(9_007_199_254_740_992),
518 Value::Integer(9_007_199_254_740_992),
519 ]
520 );
521
522 let mut rows = keyed(&vals);
523 apply_aggregate_window(&mut rows, &indices, &make_spec("max", "v", running())).unwrap();
524 assert_eq!(
525 results(&rows),
526 vec![
527 Value::Integer(9_007_199_254_740_993),
528 Value::Integer(9_007_199_254_740_993),
529 Value::Integer(9_007_199_254_740_995),
530 ]
531 );
532 }
533
534 #[test]
537 fn groups_1_preceding_1_following_sum() {
538 let mut rows = vec![
540 ("a".into(), v(json!({"n": 1i64}))),
541 ("b".into(), v(json!({"n": 1i64}))),
542 ("c".into(), v(json!({"n": 2i64}))),
543 ("d".into(), v(json!({"n": 3i64}))),
544 ("e".into(), v(json!({"n": 3i64}))),
545 ];
546 let indices: Vec<usize> = (0..5).collect();
547 let spec = make_spec(
548 "sum",
549 "n",
550 groups_frame(FrameBound::Preceding(1), FrameBound::Following(1)),
551 );
552 apply_aggregate_window(&mut rows, &indices, &spec).unwrap();
553 assert_eq!(res(&rows[0]), Value::Integer(4));
555 assert_eq!(res(&rows[1]), Value::Integer(4));
557 assert_eq!(res(&rows[2]), Value::Integer(10));
559 assert_eq!(res(&rows[3]), Value::Integer(8));
561 assert_eq!(res(&rows[4]), Value::Integer(8));
563 }
564}