1use std::collections::HashMap;
7
8use nodedb_types::Value;
9
10use super::extremum::{extremum_direction, value_replaces};
11use super::spec::{FrameBound, WindowFrame, WindowFuncSpec};
12use super::value_eval::{WindowError, cmp_values, eval_arg_for_row, order_keys_equal_v, set_cell};
13use crate::numeric_sum::ExactSum;
14
15pub(super) fn apply_v_aggregate(
16 rows: &mut [Vec<Value>],
17 indices: &[usize],
18 column_index: &HashMap<String, usize>,
19 spec: &WindowFuncSpec,
20 write_col: usize,
21) -> Result<(), WindowError> {
22 let use_running = spec.frame.mode == "range"
23 && matches!(spec.frame.start, FrameBound::UnboundedPreceding)
24 && matches!(spec.frame.end, FrameBound::CurrentRow);
25
26 if use_running {
27 apply_v_running_aggregate(rows, indices, column_index, spec, write_col)
28 } else {
29 apply_v_per_row_aggregate(rows, indices, column_index, spec, write_col)
30 }
31}
32
33fn eval_arg(
34 spec: &WindowFuncSpec,
35 row: &[Value],
36 column_index: &HashMap<String, usize>,
37) -> Result<Value, WindowError> {
38 match spec.args.first() {
39 Some(expr) => Ok(eval_arg_for_row(expr, row, column_index)?),
40 None => Ok(Value::Null),
41 }
42}
43
44fn apply_v_running_aggregate(
45 rows: &mut [Vec<Value>],
46 indices: &[usize],
47 column_index: &HashMap<String, usize>,
48 spec: &WindowFuncSpec,
49 write_col: usize,
50) -> Result<(), WindowError> {
51 let len = indices.len();
52 if len == 0 {
53 return Ok(());
54 }
55
56 let mut running_sum = ExactSum::new();
58 let mut running_count = 0u64;
59 let extremum_dir = extremum_direction(&spec.func_name);
61 let mut running_extreme: Option<Value> = None;
62 let mut peer_start = 0usize;
63
64 for pos in 0..len {
65 let i = indices[pos];
66 let val = match rows.get(i) {
67 Some(row) => eval_arg(spec, row, column_index)?,
68 None => Value::Null,
69 };
70
71 if is_window_number(&val) {
72 running_sum.add_value(&val);
73 running_count += 1;
74 if let Some(want_max) = extremum_dir
75 && value_replaces(&val, running_extreme.as_ref(), want_max)
76 {
77 running_extreme = Some(val);
78 }
79 } else if spec.func_name == "count" && (spec.args.is_empty() || !val.is_null()) {
80 running_count += 1;
83 }
84
85 let is_last_in_group = pos + 1 == len
86 || !order_keys_equal_v(rows, i, indices[pos + 1], column_index, &spec.order_by)?;
87
88 if is_last_in_group {
89 let first_val = match rows.get(indices[0]) {
90 Some(row) => eval_arg(spec, row, column_index)?,
91 None => Value::Null,
92 };
93 let last_val = match rows.get(indices[pos]) {
94 Some(row) => eval_arg(spec, row, column_index)?,
95 None => Value::Null,
96 };
97
98 let result = match spec.func_name.as_str() {
99 "sum" => running_sum.sum()?,
100 "count" => Value::Integer(running_count as i64),
101 "avg" => running_sum.avg()?,
102 "min" | "max" => running_extreme.clone().unwrap_or(Value::Null),
103 "first_value" => first_val,
104 "last_value" => last_val,
105 _ => Value::Null,
106 };
107
108 for &peer_idx in &indices[peer_start..=pos] {
109 set_cell(rows, peer_idx, write_col, result.clone());
110 }
111 peer_start = pos + 1;
112 }
113 }
114 Ok(())
115}
116
117fn apply_v_per_row_aggregate(
118 rows: &mut [Vec<Value>],
119 indices: &[usize],
120 column_index: &HashMap<String, usize>,
121 spec: &WindowFuncSpec,
122 write_col: usize,
123) -> Result<(), WindowError> {
124 let len = indices.len();
125 if len == 0 {
126 return Ok(());
127 }
128
129 let order_expr = spec.order_by.first().map(|(expr, _)| expr);
130 let order_values: Vec<Value> = indices
131 .iter()
132 .map(|&i| match (order_expr, rows.get(i)) {
133 (Some(expr), Some(row)) => Ok(eval_arg_for_row(expr, row, column_index)?),
134 _ => Ok(Value::Null),
135 })
136 .collect::<Result<Vec<_>, WindowError>>()?;
137
138 let peer_groups: Vec<usize> = if spec.frame.mode == "groups" {
139 build_v_peer_groups(&order_values)
140 } else {
141 Vec::new()
142 };
143
144 let arg_vals: Vec<Value> = indices
145 .iter()
146 .map(|&i| match rows.get(i) {
147 Some(row) => eval_arg(spec, row, column_index),
148 None => Ok(Value::Null),
149 })
150 .collect::<Result<Vec<_>, WindowError>>()?;
151 let results: Vec<Value> = (0..len)
152 .map(|pos| {
153 let (start_idx, end_idx) =
154 evaluate_v_frame_bounds(&spec.frame, pos, len, &order_values, &peer_groups);
155 aggregate_v_slice(&arg_vals, spec, start_idx, end_idx)
156 })
157 .collect::<Result<_, _>>()?;
158
159 for (pos, result) in results.into_iter().enumerate() {
160 set_cell(rows, indices[pos], write_col, result);
161 }
162 Ok(())
163}
164
165fn is_window_number(v: &Value) -> bool {
167 matches!(v, Value::Integer(_) | Value::Float(_) | Value::Decimal(_))
168}
169
170fn aggregate_v_slice(
174 arg_vals: &[Value],
175 spec: &WindowFuncSpec,
176 start_idx: usize,
177 end_idx: usize,
178) -> Result<Value, WindowError> {
179 let frame_values = arg_vals.get(start_idx..=end_idx).unwrap_or(&[]);
180 let slice_count = end_idx - start_idx + 1;
181 let frame_sum = || {
182 let mut acc = ExactSum::new();
183 for v in frame_values {
184 acc.add_value(v);
185 }
186 acc
187 };
188
189 Ok(match spec.func_name.as_str() {
190 "sum" => frame_sum().sum()?,
191 "count" => {
194 let counted = if spec.args.is_empty() {
195 slice_count
196 } else {
197 frame_values.iter().filter(|v| !v.is_null()).count()
198 };
199 Value::Integer(counted as i64)
200 }
201 "avg" => frame_sum().avg()?,
202 "min" | "max" => {
203 let want_max = spec.func_name == "max";
204 let mut best: Option<&Value> = None;
205 for candidate in frame_values.iter().filter(|v| is_window_number(v)) {
206 if value_replaces(candidate, best, want_max) {
207 best = Some(candidate);
208 }
209 }
210 best.cloned().unwrap_or(Value::Null)
211 }
212 "first_value" => arg_vals.get(start_idx).cloned().unwrap_or(Value::Null),
213 "last_value" => arg_vals.get(end_idx).cloned().unwrap_or(Value::Null),
214 _ => Value::Null,
215 })
216}
217
218fn build_v_peer_groups(order_values: &[Value]) -> Vec<usize> {
219 let mut groups = Vec::with_capacity(order_values.len());
220 let mut current_group = 0usize;
221 for (i, val) in order_values.iter().enumerate() {
222 if i > 0
223 && !matches!(
224 cmp_values(val, &order_values[i - 1]),
225 std::cmp::Ordering::Equal
226 )
227 {
228 current_group += 1;
229 }
230 groups.push(current_group);
231 }
232 groups
233}
234
235pub(super) fn evaluate_v_frame_bounds(
236 frame: &WindowFrame,
237 pos: usize,
238 len: usize,
239 order_values: &[Value],
240 peer_groups: &[usize],
241) -> (usize, usize) {
242 match frame.mode.as_str() {
243 "rows" => v_rows_bounds(&frame.start, &frame.end, pos, len),
244 "range" => v_range_bounds(&frame.start, &frame.end, pos, len, order_values),
245 "groups" => v_groups_bounds(&frame.start, &frame.end, pos, len, peer_groups),
246 _ => (0, len.saturating_sub(1)),
247 }
248}
249
250fn v_rows_bounds(start: &FrameBound, end: &FrameBound, pos: usize, len: usize) -> (usize, usize) {
251 let s = v_rows_bound_to_idx(start, pos, len);
252 let e = v_rows_bound_to_idx(end, pos, len);
253 (s.min(e), s.max(e))
254}
255
256fn v_rows_bound_to_idx(bound: &FrameBound, pos: usize, len: usize) -> usize {
257 match bound {
258 FrameBound::UnboundedPreceding => 0,
259 FrameBound::Preceding(n) => pos.saturating_sub(*n as usize),
260 FrameBound::CurrentRow => pos,
261 FrameBound::Following(n) => (pos + *n as usize).min(len.saturating_sub(1)),
262 FrameBound::UnboundedFollowing => len.saturating_sub(1),
263 }
264}
265
266fn v_range_bounds(
267 start: &FrameBound,
268 end: &FrameBound,
269 pos: usize,
270 len: usize,
271 order_values: &[Value],
272) -> (usize, usize) {
273 let current_val = order_values.get(pos).and_then(|v| v.as_f64());
274 let s = v_range_bound_to_idx(start, pos, len, order_values, current_val, true);
275 let e = v_range_bound_to_idx(end, pos, len, order_values, current_val, false);
276 (s.min(e), s.max(e))
277}
278
279fn v_range_bound_to_idx(
280 bound: &FrameBound,
281 pos: usize,
282 len: usize,
283 order_values: &[Value],
284 current_val: Option<f64>,
285 is_start: bool,
286) -> usize {
287 match bound {
288 FrameBound::UnboundedPreceding => 0,
289 FrameBound::UnboundedFollowing => len.saturating_sub(1),
290 FrameBound::CurrentRow => {
291 if is_start {
292 let mut idx = pos;
293 while idx > 0
294 && matches!(
295 cmp_values(
296 order_values.get(idx - 1).unwrap_or(&Value::Null),
297 order_values.get(pos).unwrap_or(&Value::Null),
298 ),
299 std::cmp::Ordering::Equal
300 )
301 {
302 idx -= 1;
303 }
304 idx
305 } else {
306 let mut idx = pos;
307 while idx + 1 < len
308 && matches!(
309 cmp_values(
310 order_values.get(idx + 1).unwrap_or(&Value::Null),
311 order_values.get(pos).unwrap_or(&Value::Null),
312 ),
313 std::cmp::Ordering::Equal
314 )
315 {
316 idx += 1;
317 }
318 idx
319 }
320 }
321 FrameBound::Preceding(n) => {
322 let threshold = match current_val {
323 Some(cv) => cv - *n as f64,
324 None => return pos,
325 };
326 let mut idx = 0;
327 for (i, v) in order_values.iter().enumerate() {
328 if v.as_f64().is_some_and(|fv| fv >= threshold) {
329 idx = i;
330 break;
331 }
332 idx = i + 1;
333 }
334 idx.min(len.saturating_sub(1))
335 }
336 FrameBound::Following(n) => {
337 let threshold = match current_val {
338 Some(cv) => cv + *n as f64,
339 None => return pos,
340 };
341 let mut idx = pos;
342 for (i, v) in order_values.iter().enumerate().skip(pos) {
343 if v.as_f64().is_none_or(|fv| fv > threshold) {
344 break;
345 }
346 idx = i;
347 }
348 idx.min(len.saturating_sub(1))
349 }
350 }
351}
352
353fn v_groups_bounds(
354 start: &FrameBound,
355 end: &FrameBound,
356 pos: usize,
357 len: usize,
358 peer_groups: &[usize],
359) -> (usize, usize) {
360 let current_group = peer_groups.get(pos).copied().unwrap_or(0);
361 let max_group = peer_groups.last().copied().unwrap_or(0);
362 let start_group = v_groups_bound_to_group(start, current_group, max_group);
363 let end_group = v_groups_bound_to_group(end, current_group, max_group);
364 let start_idx = peer_groups
365 .iter()
366 .position(|&g| g == start_group)
367 .unwrap_or(0);
368 let end_idx = peer_groups
369 .iter()
370 .rposition(|&g| g == end_group)
371 .unwrap_or(len.saturating_sub(1));
372 (start_idx, end_idx)
373}
374
375fn v_groups_bound_to_group(bound: &FrameBound, current_group: usize, max_group: usize) -> usize {
376 match bound {
377 FrameBound::UnboundedPreceding => 0,
378 FrameBound::UnboundedFollowing => max_group,
379 FrameBound::CurrentRow => current_group,
380 FrameBound::Preceding(n) => current_group.saturating_sub(*n as usize),
381 FrameBound::Following(n) => (current_group + *n as usize).min(max_group),
382 }
383}
384
385#[cfg(test)]
386mod tests {
387 use super::*;
388 use crate::expr::types::SqlExpr;
389
390 fn col(name: &str) -> SqlExpr {
391 SqlExpr::Column(name.to_string())
392 }
393
394 fn ci(names: &[&str]) -> HashMap<String, usize> {
395 names
396 .iter()
397 .enumerate()
398 .map(|(i, n)| (n.to_string(), i))
399 .collect()
400 }
401
402 fn rows_v(vals: &[i64]) -> Vec<Vec<Value>> {
403 vals.iter().map(|&v| vec![Value::Integer(v)]).collect()
404 }
405
406 fn agg_spec(func: &str, frame: WindowFrame, order_by: Vec<(SqlExpr, bool)>) -> WindowFuncSpec {
407 WindowFuncSpec {
408 alias: format!("w_{func}"),
409 func_name: func.to_string(),
410 args: vec![col("v")],
411 partition_by: vec![],
412 order_by,
413 frame,
414 }
415 }
416
417 fn run_agg(rows: &mut [Vec<Value>], cols: &HashMap<String, usize>, spec: &WindowFuncSpec) {
421 let write_col = rows.first().map(|r| r.len()).unwrap_or(0);
422 for row in rows.iter_mut() {
423 row.push(Value::Null);
424 }
425 let indices: Vec<usize> = (0..rows.len()).collect();
426 apply_v_aggregate(rows, &indices, cols, spec, write_col).unwrap();
427 }
428
429 fn frame(mode: &str, start: FrameBound, end: FrameBound) -> WindowFrame {
430 WindowFrame {
431 mode: mode.into(),
432 start,
433 end,
434 }
435 }
436
437 #[test]
438 fn running_sum_is_cumulative() {
439 let cols = ci(&["v"]);
442 let mut rows = rows_v(&[1, 2, 3]);
443 let s = agg_spec("sum", WindowFrame::default(), vec![(col("v"), true)]);
444 run_agg(&mut rows, &cols, &s);
445 let got: Vec<f64> = rows.iter().map(|r| r[1].as_f64().unwrap()).collect();
446 assert_eq!(got, vec![1.0, 3.0, 6.0]);
447 }
448
449 #[test]
450 fn running_sum_shares_value_across_peers() {
451 let cols = ci(&["v"]);
454 let mut rows = rows_v(&[5, 5, 9]);
455 let s = agg_spec("sum", WindowFrame::default(), vec![(col("v"), true)]);
456 run_agg(&mut rows, &cols, &s);
457 let got: Vec<f64> = rows.iter().map(|r| r[1].as_f64().unwrap()).collect();
458 assert_eq!(got, vec![10.0, 10.0, 19.0]);
459 }
460
461 #[test]
462 fn rows_frame_sliding_sum() {
463 let cols = ci(&["v"]);
464 let mut rows = rows_v(&[10, 20, 30]);
465 let s = agg_spec(
466 "sum",
467 frame("rows", FrameBound::Preceding(1), FrameBound::CurrentRow),
468 vec![(col("v"), true)],
469 );
470 run_agg(&mut rows, &cols, &s);
471 let got: Vec<f64> = rows.iter().map(|r| r[1].as_f64().unwrap()).collect();
472 assert_eq!(got, vec![10.0, 30.0, 50.0]);
473 }
474
475 #[test]
476 fn rows_frame_count_and_avg() {
477 let cols = ci(&["v"]);
478 let f = frame(
479 "rows",
480 FrameBound::UnboundedPreceding,
481 FrameBound::CurrentRow,
482 );
483
484 let mut rows = rows_v(&[4, 8, 12]);
485 let cnt = agg_spec("count", f.clone(), vec![(col("v"), true)]);
486 run_agg(&mut rows, &cols, &cnt);
487 let counts: Vec<i64> = rows
488 .iter()
489 .map(|r| match r[1] {
490 Value::Integer(n) => n,
491 _ => panic!("count must be integer"),
492 })
493 .collect();
494 assert_eq!(counts, vec![1, 2, 3]);
495
496 let mut rows = rows_v(&[4, 8, 12]);
497 let avg = agg_spec("avg", f, vec![(col("v"), true)]);
498 run_agg(&mut rows, &cols, &avg);
499 let avgs: Vec<f64> = rows.iter().map(|r| r[1].as_f64().unwrap()).collect();
500 assert_eq!(avgs, vec![4.0, 6.0, 8.0]);
501 }
502
503 #[test]
504 fn rows_frame_min_max() {
505 let cols = ci(&["v"]);
506 let f = frame(
507 "rows",
508 FrameBound::UnboundedPreceding,
509 FrameBound::UnboundedFollowing,
510 );
511
512 let mut rows = rows_v(&[3, 1, 2]);
513 let mn = agg_spec("min", f.clone(), vec![]);
514 run_agg(&mut rows, &cols, &mn);
515 assert!((rows[0][1].as_f64().unwrap() - 1.0).abs() < 1e-9);
516
517 let mut rows = rows_v(&[3, 1, 2]);
518 let mx = agg_spec("max", f, vec![]);
519 run_agg(&mut rows, &cols, &mx);
520 assert!((rows[0][1].as_f64().unwrap() - 3.0).abs() < 1e-9);
521 }
522
523 fn whole_partition() -> WindowFrame {
524 frame(
525 "rows",
526 FrameBound::UnboundedPreceding,
527 FrameBound::UnboundedFollowing,
528 )
529 }
530
531 #[test]
532 fn frame_min_max_keep_integers_above_2_pow_53_exact() {
533 let cols = ci(&["v"]);
534 let vals = [9_007_199_254_740_993, 9_007_199_254_740_992];
535
536 let mut rows = rows_v(&vals);
537 run_agg(
538 &mut rows,
539 &cols,
540 &agg_spec("min", whole_partition(), vec![]),
541 );
542 assert_eq!(rows[0][1], Value::Integer(9_007_199_254_740_992));
543
544 let mut rows = rows_v(&vals);
545 run_agg(
546 &mut rows,
547 &cols,
548 &agg_spec("max", whole_partition(), vec![]),
549 );
550 assert_eq!(rows[0][1], Value::Integer(9_007_199_254_740_993));
551 }
552
553 #[test]
554 fn sum_keeps_integers_above_2_pow_53_exact() {
555 let cols = ci(&["v"]);
556 let vals = [9_007_199_254_740_993, 9_007_199_254_740_992];
557
558 let mut rows = rows_v(&vals);
559 run_agg(
560 &mut rows,
561 &cols,
562 &agg_spec("sum", whole_partition(), vec![]),
563 );
564 assert_eq!(rows[0][1], Value::Integer(18_014_398_509_481_985));
565
566 let mut rows = rows_v(&[9_007_199_254_740_993, 1]);
567 let running = agg_spec("sum", WindowFrame::default(), vec![(col("v"), false)]);
568 run_agg(&mut rows, &cols, &running);
569 assert_eq!(rows[0][1], Value::Integer(9_007_199_254_740_993));
570 assert_eq!(rows[1][1], Value::Integer(9_007_199_254_740_994));
571
572 let mut rows = rows_v(&vals);
573 run_agg(
574 &mut rows,
575 &cols,
576 &agg_spec("avg", whole_partition(), vec![]),
577 );
578 assert_eq!(rows[0][1], Value::Float(9_007_199_254_740_992.0));
579 }
580
581 #[test]
582 fn sum_past_i64_is_decimal_and_u64_counts() {
583 let cols = ci(&["v"]);
584 let mut rows = vec![
585 vec![Value::Integer(i64::MAX)],
586 vec![Value::from_u64(u64::MAX)],
587 ];
588 run_agg(
589 &mut rows,
590 &cols,
591 &agg_spec("sum", whole_partition(), vec![]),
592 );
593 assert_eq!(
594 rows[0][1],
595 Value::Decimal(rust_decimal::Decimal::from_i128_with_scale(
596 i128::from(i64::MAX) + i128::from(u64::MAX),
597 0
598 ))
599 );
600
601 let mut rows = vec![
602 vec![Value::Integer(i64::MAX)],
603 vec![Value::from_u64(u64::MAX)],
604 ];
605 run_agg(
606 &mut rows,
607 &cols,
608 &agg_spec("max", whole_partition(), vec![]),
609 );
610 assert_eq!(rows[0][1], Value::from_u64(u64::MAX));
611 }
612
613 #[test]
614 fn sum_mixed_int_float_is_float_and_nan_is_the_largest_extreme() {
615 let cols = ci(&["v"]);
616 let mixed = || vec![vec![Value::Integer(2)], vec![Value::Float(0.5)]];
617 let mut rows = mixed();
618 run_agg(
619 &mut rows,
620 &cols,
621 &agg_spec("sum", whole_partition(), vec![]),
622 );
623 assert_eq!(rows[0][1], Value::Float(2.5));
624
625 let with_nan = || {
626 vec![
627 vec![Value::Float(f64::NAN)],
628 vec![Value::Integer(4)],
629 vec![Value::Integer(9)],
630 ]
631 };
632 let mut rows = with_nan();
633 run_agg(
634 &mut rows,
635 &cols,
636 &agg_spec("min", whole_partition(), vec![]),
637 );
638 assert_eq!(rows[0][1], Value::Integer(4));
639 let mut rows = with_nan();
641 run_agg(
642 &mut rows,
643 &cols,
644 &agg_spec("max", whole_partition(), vec![]),
645 );
646 assert!(matches!(rows[0][1], Value::Float(f) if f.is_nan()));
647 }
648
649 #[test]
650 fn frame_min_max_over_nanosecond_timestamps() {
651 let cols = ci(&["v"]);
652 let ts = [
653 1_700_000_000_000_000_001,
654 1_700_000_000_000_000_003,
655 1_700_000_000_000_000_002,
656 ];
657
658 let mut rows = rows_v(&ts);
659 run_agg(
660 &mut rows,
661 &cols,
662 &agg_spec("min", whole_partition(), vec![]),
663 );
664 assert_eq!(rows[1][1], Value::Integer(1_700_000_000_000_000_001));
665
666 let mut rows = rows_v(&ts);
667 run_agg(
668 &mut rows,
669 &cols,
670 &agg_spec("max", whole_partition(), vec![]),
671 );
672 assert_eq!(rows[1][1], Value::Integer(1_700_000_000_000_000_003));
673 }
674
675 #[test]
676 fn frame_min_max_mixed_int_float_return_original_type() {
677 let cols = ci(&["v"]);
678 let mixed = || {
679 vec![
680 vec![Value::Integer(9_007_199_254_740_993)],
681 vec![Value::Float(9_007_199_254_740_992.0)],
682 vec![Value::Float(1.5)],
683 vec![Value::Integer(2)],
684 ]
685 };
686
687 let mut rows = mixed();
688 run_agg(
689 &mut rows,
690 &cols,
691 &agg_spec("min", whole_partition(), vec![]),
692 );
693 assert_eq!(rows[0][1], Value::Float(1.5));
694
695 let mut rows = mixed();
696 run_agg(
697 &mut rows,
698 &cols,
699 &agg_spec("max", whole_partition(), vec![]),
700 );
701 assert_eq!(rows[0][1], Value::Integer(9_007_199_254_740_993));
702 }
703
704 #[test]
705 fn running_min_max_keep_integers_exact() {
706 let cols = ci(&["v", "k"]);
708 let keyed = || {
709 vec![
710 vec![Value::Integer(9_007_199_254_740_993), Value::Integer(1)],
711 vec![Value::Integer(9_007_199_254_740_992), Value::Integer(2)],
712 vec![Value::Integer(9_007_199_254_740_995), Value::Integer(3)],
713 ]
714 };
715 let order = vec![(col("k"), true)];
716
717 let mut rows = keyed();
718 run_agg(
719 &mut rows,
720 &cols,
721 &agg_spec("min", WindowFrame::default(), order.clone()),
722 );
723 let mins: Vec<Value> = rows.iter().map(|r| r[2].clone()).collect();
724 assert_eq!(
725 mins,
726 vec![
727 Value::Integer(9_007_199_254_740_993),
728 Value::Integer(9_007_199_254_740_992),
729 Value::Integer(9_007_199_254_740_992),
730 ]
731 );
732
733 let mut rows = keyed();
734 run_agg(
735 &mut rows,
736 &cols,
737 &agg_spec("max", WindowFrame::default(), order),
738 );
739 let maxes: Vec<Value> = rows.iter().map(|r| r[2].clone()).collect();
740 assert_eq!(
741 maxes,
742 vec![
743 Value::Integer(9_007_199_254_740_993),
744 Value::Integer(9_007_199_254_740_993),
745 Value::Integer(9_007_199_254_740_995),
746 ]
747 );
748 }
749
750 #[test]
751 fn count_expr_skips_null_arguments() {
752 let cols = ci(&["v"]);
753 let rows_with_null = || {
754 vec![
755 vec![Value::Integer(1)],
756 vec![Value::Null],
757 vec![Value::String("x".into())],
758 ]
759 };
760
761 let mut rows = rows_with_null();
762 run_agg(
763 &mut rows,
764 &cols,
765 &agg_spec("count", whole_partition(), vec![]),
766 );
767 assert_eq!(rows[0][1], Value::Integer(2));
768
769 let mut rows = rows_with_null();
770 let mut star = agg_spec("count", whole_partition(), vec![]);
771 star.args.clear();
772 run_agg(&mut rows, &cols, &star);
773 assert_eq!(rows[0][1], Value::Integer(3));
774
775 let mut rows = rows_with_null();
776 run_agg(
777 &mut rows,
778 &cols,
779 &agg_spec("count", WindowFrame::default(), vec![]),
780 );
781 assert_eq!(rows[2][1], Value::Integer(2));
782 }
783
784 #[test]
785 fn first_and_last_value() {
786 let cols = ci(&["v"]);
787 let f = frame(
788 "rows",
789 FrameBound::UnboundedPreceding,
790 FrameBound::UnboundedFollowing,
791 );
792
793 let mut rows = rows_v(&[7, 8, 9]);
794 let fv = agg_spec("first_value", f.clone(), vec![]);
795 run_agg(&mut rows, &cols, &fv);
796 assert_eq!(rows[2][1].as_f64().unwrap() as i64, 7);
797
798 let mut rows = rows_v(&[7, 8, 9]);
799 let lv = agg_spec("last_value", f, vec![]);
800 run_agg(&mut rows, &cols, &lv);
801 assert_eq!(rows[0][1].as_f64().unwrap() as i64, 9);
802 }
803
804 #[test]
805 fn rows_bounds_resolution() {
806 let order = vec![];
807 let groups = vec![];
808 let f = frame("rows", FrameBound::Preceding(1), FrameBound::Following(1));
809 assert_eq!(evaluate_v_frame_bounds(&f, 2, 5, &order, &groups), (1, 3));
811 assert_eq!(evaluate_v_frame_bounds(&f, 0, 5, &order, &groups), (0, 1));
813 assert_eq!(evaluate_v_frame_bounds(&f, 4, 5, &order, &groups), (3, 4));
815 }
816
817 #[test]
818 fn range_bounds_expand_over_peers() {
819 let order = vec![Value::Integer(10), Value::Integer(10), Value::Integer(20)];
821 let f = frame(
822 "range",
823 FrameBound::UnboundedPreceding,
824 FrameBound::CurrentRow,
825 );
826 assert_eq!(evaluate_v_frame_bounds(&f, 0, 3, &order, &[]), (0, 1));
828 assert_eq!(evaluate_v_frame_bounds(&f, 2, 3, &order, &[]), (0, 2));
830 }
831
832 #[test]
833 fn groups_bounds_resolution() {
834 let peer_groups = vec![0usize, 0, 1, 2];
837 let order = vec![
838 Value::Integer(1),
839 Value::Integer(1),
840 Value::Integer(2),
841 Value::Integer(3),
842 ];
843 let f = frame("groups", FrameBound::Preceding(1), FrameBound::CurrentRow);
844 assert_eq!(
845 evaluate_v_frame_bounds(&f, 2, 4, &order, &peer_groups),
846 (0, 2)
847 );
848 }
849}