Per-row frame evaluator. For each row position `pos` in the partition: 1. Resolve the concrete `[start_idx, end_idx]` frame slice via `evaluate_frame_bounds`. 2. Aggregate `field` over `indices[start_idx..=end_idx]`. 3. Write the result back under `spec.alias`.
(
rows: &mut [(String, serde_json::Value)],
indices: &[usize],
spec: &WindowFuncSpec,
field: &str,
)
| 54 | /// 2. Aggregate `field` over `indices[start_idx..=end_idx]`. |
| 55 | /// 3. Write the result back under `spec.alias`. |
| 56 | fn per_row_aggregate( |
| 57 | rows: &mut [(String, serde_json::Value)], |
| 58 | indices: &[usize], |
| 59 | spec: &WindowFuncSpec, |
| 60 | field: &str, |
| 61 | ) { |
| 62 | let len = indices.len(); |
| 63 | if len == 0 { |
| 64 | return; |
| 65 | } |
| 66 | |
| 67 | // Extract order-by values for RANGE numeric offsets. |
| 68 | let order_expr = spec.order_by.first().map(|(expr, _)| expr); |
| 69 | let order_values: Vec<serde_json::Value> = indices |
| 70 | .iter() |
| 71 | .map(|&i| { |
| 72 | order_expr |
| 73 | .map(|expr| super::helpers::eval_expr_on_json(expr, &rows[i].1)) |
| 74 | .unwrap_or(serde_json::Value::Null) |
| 75 | }) |
| 76 | .collect(); |
| 77 | |
| 78 | // Peer groups needed for GROUPS mode (and for RANGE CurrentRow peer |
| 79 | // awareness — reused from the frame module which handles both). |
| 80 | let peer_groups: Vec<usize> = if spec.frame.mode == "groups" { |
| 81 | build_peer_groups(&order_values) |
| 82 | } else { |
| 83 | Vec::new() |
| 84 | }; |
| 85 | |
| 86 | // Pre-collect all numeric values to avoid repeated borrow issues. |
| 87 | let all_vals: Vec<Option<f64>> = indices |
| 88 | .iter() |
| 89 | .map(|&i| as_f64(&get_field(&rows[i].1, field))) |
| 90 | .collect(); |
| 91 | |
| 92 | // We need to write into `rows` after computing each result; collect |
| 93 | // results first so we only borrow `rows` immutably during computation. |
| 94 | let results: Vec<serde_json::Value> = (0..len) |
| 95 | .map(|pos| { |
| 96 | let (start_idx, end_idx) = |
| 97 | evaluate_frame_bounds(&spec.frame, pos, len, &order_values, &peer_groups); |
| 98 | |
| 99 | aggregate_slice(&all_vals, indices, rows, field, spec, start_idx, end_idx) |
| 100 | }) |
| 101 | .collect(); |
| 102 | |
| 103 | for (pos, result) in results.into_iter().enumerate() { |
| 104 | let row_idx = indices[pos]; |
| 105 | set_window_col(&mut rows[row_idx].1, &spec.alias, result); |
| 106 | } |
| 107 | } |
| 108 | |
| 109 | /// Aggregate `field` over the slice `indices[start_idx..=end_idx]`. |
| 110 | fn aggregate_slice( |
no test coverage detected