1use std::collections::BTreeMap;
2
3use linkrs_core::error::QueryError;
4use linkrs_core::types::expr::Expression;
5use linkrs_core::value::NullType;
6use linkrs_core::Value;
7
8use crate::executor::base::{MemoryBudget, MemoryTracker};
9use crate::executor::expression::evaluator::ExpressionEvaluator;
10use crate::executor::streaming::chunk::DataChunk;
11use crate::executor::streaming::executor::{SortDirection, StreamingExecutor, ValueRowContext};
12use crate::executor::streaming::spill::{
13 finalize_partitions_with_runtime, HashPartitionConfig, HashPartitionSpiller, SpillManager,
14};
15
16use super::helpers::{emit_batch_slice, BlockingContext};
17use super::window::{
18 compute_window_partition_result, sort_partition_rows, WindowFunctionState, WindowState,
19};
20
21pub(super) fn open_window_function(state: &mut Option<WindowFunctionState>) {
22 *state = Some(WindowFunctionState {
23 batch: linkrs_core::columnar::MaterializedBatch::new(0, 0),
24 col_names: vec![],
25 result_batch: linkrs_core::columnar::MaterializedBatch::new(0, 0),
26 emitted_offset: 0,
27 partition_spiller: None,
28 spilled_runs: vec![],
29 current_partition: 0,
30 has_spilled: false,
31 output_complete: false,
32 });
33}
34
35pub(super) fn open_window(state: &mut Option<WindowState>) {
36 *state = Some(WindowState {
37 batch: linkrs_core::columnar::MaterializedBatch::new(0, 0),
38 col_names: vec![],
39 result_batch: linkrs_core::columnar::MaterializedBatch::new(0, 0),
40 emitted_offset: 0,
41 partition_spiller: None,
42 spilled_runs: vec![],
43 current_partition: 0,
44 has_spilled: false,
45 output_complete: false,
46 });
47}
48
49#[allow(clippy::too_many_arguments)]
50pub(super) fn next_window_function(
51 window_exprs: &[Expression],
52 partition_by_exprs: &[Expression],
53 order_by_exprs: &[Expression],
54 order_by_directions: &[SortDirection],
55 memory_tracker: &mut MemoryTracker,
56 state: &mut WindowFunctionState,
57 ctx: &BlockingContext<'_>,
58 input: &mut StreamingExecutor,
59) -> Result<Option<DataChunk>, QueryError> {
60 use crate::executor::streaming::spill::RunReader;
61
62 let eval_partition_key = |row: &[Value], col_names: &[String]| -> Vec<Value> {
63 if partition_by_exprs.is_empty() {
64 return vec![Value::Null(NullType::Null)];
65 }
66 let mut key = Vec::with_capacity(partition_by_exprs.len());
67 for expr in partition_by_exprs.iter() {
68 let mut ctx = ValueRowContext::from_names(row.to_vec(), col_names.to_vec());
69 key.push(
70 ExpressionEvaluator::evaluate(expr, &mut ctx)
71 .unwrap_or(Value::Null(NullType::Null)),
72 );
73 }
74 key
75 };
76
77 loop {
78 if state.output_complete {
79 return Ok(None);
80 }
81
82 if state.emitted_offset < state.result_batch.num_rows() {
84 return Ok(Some(emit_batch_slice(
85 &state.result_batch,
86 &mut state.emitted_offset,
87 ctx,
88 )));
89 }
90 if state.emitted_offset > 0 {
91 state.emitted_offset = 0;
92 state.result_batch.clear();
93 if !state.has_spilled || state.current_partition >= state.spilled_runs.len() {
94 state.output_complete = true;
95 return Ok(None);
96 }
97 }
98
99 if state.has_spilled && state.partition_spiller.is_none() {
101 while state.current_partition < state.spilled_runs.len() {
102 if let Some(rt) = ctx.runtime.as_ref() {
103 rt.ensure_not_cancelled()?;
104 }
105
106 let run = match &state.spilled_runs[state.current_partition] {
107 Some(r) => r,
108 None => {
109 state.current_partition += 1;
110 continue;
111 }
112 };
113
114 let mut reader = RunReader::open(run)?;
115 let mut partitions: BTreeMap<Vec<Value>, Vec<(usize, Vec<Value>)>> =
116 BTreeMap::new();
117 let mut run_row_count: u64 = 0;
118 while let Some(row) = reader.read_row()? {
119 memory_tracker.try_reserve_row(&row)?;
120 let partition_key = eval_partition_key(&row, &state.col_names);
121 partitions
122 .entry(partition_key)
123 .or_default()
124 .push((run_row_count as usize, row));
125 run_row_count += 1;
126 }
127
128 let mut result_rows = Vec::with_capacity(run_row_count as usize);
129 for (_key, mut partition_rows) in partitions {
130 sort_partition_rows(
131 &mut partition_rows,
132 &state.col_names,
133 order_by_exprs,
134 order_by_directions,
135 );
136 result_rows.extend(compute_window_partition_result(
137 &partition_rows,
138 &state.col_names,
139 window_exprs,
140 ));
141 }
142
143 let _ = std::fs::remove_file(&run.path);
144 state.current_partition += 1;
145 memory_tracker.reset();
146
147 if !result_rows.is_empty() {
148 state.result_batch.clear();
149 state.emitted_offset = 0;
150 for result_row in result_rows {
151 state.result_batch.append_row(result_row);
152 }
153 return Ok(Some(emit_batch_slice(
154 &state.result_batch,
155 &mut state.emitted_offset,
156 ctx,
157 )));
158 }
159 }
160 state.output_complete = true;
161 return Ok(None);
162 }
163
164 let mut accumulating = true;
166 while accumulating {
167 match input.advance()? {
168 Some(mut chunk) => {
169 chunk.normalize_for_opaque("WindowFunction");
170 if let Some(rt) = ctx.runtime.as_ref() {
171 rt.ensure_not_cancelled()?;
172 }
173 if state.col_names.is_empty() {
174 state.col_names = chunk.col_names();
175 }
176 for row in chunk.rows {
177 if let Some(ref mut spiller) = state.partition_spiller {
178 let manager = ctx
179 .runtime
180 .as_ref()
181 .and_then(|rt| rt.get_spill_manager())
182 .ok_or_else(|| {
183 QueryError::execution("Spill manager not available".to_string())
184 })?;
185 let partition_key = eval_partition_key(&row, &state.col_names);
186 let p = crate::executor::streaming::spill::hash_row_partition(
187 &partition_key,
188 spiller.num_partitions(),
189 ) as usize;
190 spiller.insert_row_to_partition(&row, p, &manager)?;
191 continue;
192 }
193 if let Err(e) = memory_tracker.try_reserve_row(&row) {
194 if let Some(sm) =
195 ctx.runtime.as_ref().and_then(|rt| rt.get_spill_manager())
196 {
197 let config = HashPartitionConfig::default();
198 let num_partitions = config.num_partitions;
199 let mut spiller = HashPartitionSpiller::new(config, &sm, 0)?;
200
201 for pending in state.batch.into_rows() {
202 let partition_key =
203 eval_partition_key(&pending, &state.col_names);
204 let p = crate::executor::streaming::spill::hash_row_partition(
205 &partition_key,
206 num_partitions,
207 ) as usize;
208 spiller.insert_row_to_partition(&pending, p, &sm)?;
209 memory_tracker
210 .release(MemoryBudget::estimate_row_memory(&pending));
211 }
212
213 let partition_key = eval_partition_key(&row, &state.col_names);
214 let p = crate::executor::streaming::spill::hash_row_partition(
215 &partition_key,
216 num_partitions,
217 ) as usize;
218 spiller.insert_row_to_partition(&row, p, &sm)?;
219
220 state.partition_spiller = Some(spiller);
221 state.has_spilled = true;
222 continue;
223 } else {
224 return Err(e);
225 }
226 }
227 state.batch.append_row(row);
228 }
229 }
230 None => {
231 accumulating = false;
232 }
233 }
234 }
235
236 if state.partition_spiller.is_some() {
238 let runs = finalize_partitions_with_runtime(
239 state.partition_spiller.take().unwrap(),
240 ctx.runtime.as_ref(),
241 )?;
242 state.spilled_runs = runs;
243 state.current_partition = 0;
244 continue;
245 }
246
247 if state.batch.is_empty() {
249 state.output_complete = true;
250 return Ok(None);
251 }
252 let mut partitions: BTreeMap<Vec<Value>, Vec<(usize, Vec<Value>)>> = BTreeMap::new();
253 for (idx, row) in state.batch.into_rows().into_iter().enumerate() {
254 let partition_key = eval_partition_key(&row, &state.col_names);
255 partitions
256 .entry(partition_key)
257 .or_default()
258 .push((idx, row));
259 }
260 let mut result_rows = Vec::new();
261 for (_key, mut partition_rows) in partitions {
262 sort_partition_rows(
263 &mut partition_rows,
264 &state.col_names,
265 order_by_exprs,
266 order_by_directions,
267 );
268 result_rows.extend(compute_window_partition_result(
269 &partition_rows,
270 &state.col_names,
271 window_exprs,
272 ));
273 }
274
275 state.result_batch.clear();
276 state.emitted_offset = 0;
277 for result_row in result_rows {
278 state.result_batch.append_row(result_row);
279 }
280 if state.result_batch.is_empty() {
281 state.output_complete = true;
282 return Ok(None);
283 }
284 return Ok(Some(emit_batch_slice(
285 &state.result_batch,
286 &mut state.emitted_offset,
287 ctx,
288 )));
289 }
290}
291
292#[allow(clippy::too_many_arguments)]
293pub(super) fn next_window(
294 window_exprs: &[Expression],
295 partition_by_exprs: &[Expression],
296 order_by_exprs: &[Expression],
297 order_by_directions: &[SortDirection],
298 memory_tracker: &mut MemoryTracker,
299 state: &mut WindowState,
300 ctx: &BlockingContext<'_>,
301 input: &mut StreamingExecutor,
302) -> Result<Option<DataChunk>, QueryError> {
303 use crate::executor::streaming::spill::RunReader;
304
305 let eval_partition_key = |row: &[Value], col_names: &[String]| -> Vec<Value> {
306 if partition_by_exprs.is_empty() {
307 return vec![Value::Null(NullType::Null)];
308 }
309 let mut key = Vec::with_capacity(partition_by_exprs.len());
310 for expr in partition_by_exprs.iter() {
311 let mut ctx = ValueRowContext::from_names(row.to_vec(), col_names.to_vec());
312 key.push(
313 ExpressionEvaluator::evaluate(expr, &mut ctx)
314 .unwrap_or(Value::Null(NullType::Null)),
315 );
316 }
317 key
318 };
319
320 loop {
321 if state.output_complete {
322 return Ok(None);
323 }
324
325 if state.emitted_offset < state.result_batch.num_rows() {
327 return Ok(Some(emit_batch_slice(
328 &state.result_batch,
329 &mut state.emitted_offset,
330 ctx,
331 )));
332 }
333 if state.emitted_offset > 0 {
334 state.emitted_offset = 0;
335 state.result_batch.clear();
336 if !state.has_spilled || state.current_partition >= state.spilled_runs.len() {
337 state.output_complete = true;
338 return Ok(None);
339 }
340 }
341
342 if state.has_spilled && state.partition_spiller.is_none() {
344 while state.current_partition < state.spilled_runs.len() {
345 if let Some(rt) = ctx.runtime.as_ref() {
346 rt.ensure_not_cancelled()?;
347 }
348
349 let run = match &state.spilled_runs[state.current_partition] {
350 Some(r) => r,
351 None => {
352 state.current_partition += 1;
353 continue;
354 }
355 };
356
357 let mut reader = RunReader::open(run)?;
358 let mut partitions: BTreeMap<Vec<Value>, Vec<(usize, Vec<Value>)>> =
359 BTreeMap::new();
360 let mut run_row_count: u64 = 0;
361 while let Some(row) = reader.read_row()? {
362 memory_tracker.try_reserve_row(&row)?;
363 let partition_key = eval_partition_key(&row, &state.col_names);
364 partitions
365 .entry(partition_key)
366 .or_default()
367 .push((run_row_count as usize, row));
368 run_row_count += 1;
369 }
370
371 let mut result_rows = Vec::with_capacity(run_row_count as usize);
372 for (_key, mut partition_rows) in partitions {
373 sort_partition_rows(
374 &mut partition_rows,
375 &state.col_names,
376 order_by_exprs,
377 order_by_directions,
378 );
379 result_rows.extend(compute_window_partition_result(
380 &partition_rows,
381 &state.col_names,
382 window_exprs,
383 ));
384 }
385
386 let _ = std::fs::remove_file(&run.path);
387 state.current_partition += 1;
388 memory_tracker.reset();
389
390 if !result_rows.is_empty() {
391 state.result_batch.clear();
392 state.emitted_offset = 0;
393 for result_row in result_rows {
394 state.result_batch.append_row(result_row);
395 }
396 return Ok(Some(emit_batch_slice(
397 &state.result_batch,
398 &mut state.emitted_offset,
399 ctx,
400 )));
401 }
402 }
403 state.output_complete = true;
404 return Ok(None);
405 }
406
407 let mut accumulating = true;
409 while accumulating {
410 match input.advance()? {
411 Some(mut chunk) => {
412 chunk.normalize_for_opaque("Window");
413 if let Some(rt) = ctx.runtime.as_ref() {
414 rt.ensure_not_cancelled()?;
415 }
416 if state.col_names.is_empty() {
417 state.col_names = chunk.col_names();
418 }
419 for row in chunk.rows {
420 if let Some(ref mut spiller) = state.partition_spiller {
421 let manager = ctx
422 .runtime
423 .as_ref()
424 .and_then(|rt| rt.get_spill_manager())
425 .ok_or_else(|| {
426 QueryError::execution("Spill manager not available".to_string())
427 })?;
428 let partition_key = eval_partition_key(&row, &state.col_names);
429 let p = crate::executor::streaming::spill::hash_row_partition(
430 &partition_key,
431 spiller.num_partitions(),
432 ) as usize;
433 spiller.insert_row_to_partition(&row, p, &manager)?;
434 continue;
435 }
436 if let Err(e) = memory_tracker.try_reserve_row(&row) {
437 if let Some(sm) =
438 ctx.runtime.as_ref().and_then(|rt| rt.get_spill_manager())
439 {
440 let config = HashPartitionConfig::default();
441 let num_partitions = config.num_partitions;
442 let mut spiller = HashPartitionSpiller::new(config, &sm, 0)?;
443
444 for pending in state.batch.into_rows() {
445 let partition_key =
446 eval_partition_key(&pending, &state.col_names);
447 let p = crate::executor::streaming::spill::hash_row_partition(
448 &partition_key,
449 num_partitions,
450 ) as usize;
451 spiller.insert_row_to_partition(&pending, p, &sm)?;
452 memory_tracker
453 .release(MemoryBudget::estimate_row_memory(&pending));
454 }
455
456 let partition_key = eval_partition_key(&row, &state.col_names);
457 let p = crate::executor::streaming::spill::hash_row_partition(
458 &partition_key,
459 num_partitions,
460 ) as usize;
461 spiller.insert_row_to_partition(&row, p, &sm)?;
462
463 state.partition_spiller = Some(spiller);
464 state.has_spilled = true;
465 continue;
466 } else {
467 return Err(e);
468 }
469 }
470 state.batch.append_row(row);
471 }
472 }
473 None => {
474 accumulating = false;
475 }
476 }
477 }
478
479 if state.partition_spiller.is_some() {
481 let runs = finalize_partitions_with_runtime(
482 state.partition_spiller.take().unwrap(),
483 ctx.runtime.as_ref(),
484 )?;
485 state.spilled_runs = runs;
486 state.current_partition = 0;
487 continue;
488 }
489
490 if state.batch.is_empty() {
492 state.output_complete = true;
493 return Ok(None);
494 }
495 let mut partitions: BTreeMap<Vec<Value>, Vec<(usize, Vec<Value>)>> = BTreeMap::new();
496 for (idx, row) in state.batch.into_rows().into_iter().enumerate() {
497 let partition_key = eval_partition_key(&row, &state.col_names);
498 partitions
499 .entry(partition_key)
500 .or_default()
501 .push((idx, row));
502 }
503 let mut result_rows = Vec::new();
504 for (_key, mut partition_rows) in partitions {
505 sort_partition_rows(
506 &mut partition_rows,
507 &state.col_names,
508 order_by_exprs,
509 order_by_directions,
510 );
511 result_rows.extend(compute_window_partition_result(
512 &partition_rows,
513 &state.col_names,
514 window_exprs,
515 ));
516 }
517
518 state.result_batch.clear();
519 state.emitted_offset = 0;
520 for result_row in result_rows {
521 state.result_batch.append_row(result_row);
522 }
523 if state.result_batch.is_empty() {
524 state.output_complete = true;
525 return Ok(None);
526 }
527 return Ok(Some(emit_batch_slice(
528 &state.result_batch,
529 &mut state.emitted_offset,
530 ctx,
531 )));
532 }
533}
534
535pub(super) fn close_window_function(state: &mut Option<WindowFunctionState>) {
536 if let Some(ref s) = state {
537 for r in s.spilled_runs.iter().flatten() {
538 let _ = std::fs::remove_file(&r.path);
539 }
540 }
541 *state = None;
542}
543
544pub(super) fn close_window(state: &mut Option<WindowState>) {
545 if let Some(ref s) = state {
546 for r in s.spilled_runs.iter().flatten() {
547 let _ = std::fs::remove_file(&r.path);
548 }
549 }
550 *state = None;
551}
552
553pub(super) fn spill_window_function(
554 state: &mut WindowFunctionState,
555 memory_tracker: &mut MemoryTracker,
556 partition_by_exprs: &[Expression],
557 sm: &SpillManager,
558) -> Result<(), QueryError> {
559 if state.partition_spiller.is_none() && !state.batch.is_empty() {
560 let config = HashPartitionConfig::default();
561 let num_partitions = config.num_partitions;
562 let mut spiller = HashPartitionSpiller::new(config, sm, 0)?;
563 for row in state.batch.into_rows() {
564 let mut partition_key = Vec::new();
565 if partition_by_exprs.is_empty() {
566 partition_key.push(Value::Null(NullType::Null));
567 } else {
568 for expr in partition_by_exprs.iter() {
569 let mut ctx = ValueRowContext::from_names(row.clone(), state.col_names.clone());
570 partition_key.push(
571 ExpressionEvaluator::evaluate(expr, &mut ctx)
572 .unwrap_or(Value::Null(NullType::Null)),
573 );
574 }
575 }
576 let p = crate::executor::streaming::spill::hash_row_partition(
577 &partition_key,
578 num_partitions,
579 ) as usize;
580 spiller.insert_row_to_partition(&row, p, sm)?;
581 memory_tracker.release(MemoryBudget::estimate_row_memory(&row));
582 }
583 state.partition_spiller = Some(spiller);
584 state.has_spilled = true;
585 }
586 Ok(())
587}
588
589pub(super) fn spill_window(
590 state: &mut WindowState,
591 memory_tracker: &mut MemoryTracker,
592 partition_by_exprs: &[Expression],
593 sm: &SpillManager,
594) -> Result<(), QueryError> {
595 if state.partition_spiller.is_none() && !state.batch.is_empty() {
596 let config = HashPartitionConfig::default();
597 let num_partitions = config.num_partitions;
598 let mut spiller = HashPartitionSpiller::new(config, sm, 0)?;
599 for row in state.batch.into_rows() {
600 let mut partition_key = Vec::new();
601 if partition_by_exprs.is_empty() {
602 partition_key.push(Value::Null(NullType::Null));
603 } else {
604 for expr in partition_by_exprs.iter() {
605 let mut ctx = ValueRowContext::from_names(row.clone(), state.col_names.clone());
606 partition_key.push(
607 ExpressionEvaluator::evaluate(expr, &mut ctx)
608 .unwrap_or(Value::Null(NullType::Null)),
609 );
610 }
611 }
612 let p = crate::executor::streaming::spill::hash_row_partition(
613 &partition_key,
614 num_partitions,
615 ) as usize;
616 spiller.insert_row_to_partition(&row, p, sm)?;
617 memory_tracker.release(MemoryBudget::estimate_row_memory(&row));
618 }
619 state.partition_spiller = Some(spiller);
620 state.has_spilled = true;
621 }
622 Ok(())
623}