1use crate::algebra::{Binding, Expression, Solution, Term, TriplePattern, Variable};
6use anyhow::{anyhow, Result};
7use oxirs_core::model::NamedNode;
8use serde::{Deserialize, Serialize};
9use std::collections::{HashMap, VecDeque};
10use std::io::BufRead;
11use std::path::PathBuf;
12use std::sync::{Arc, Mutex};
13use std::time::{Duration, Instant};
14use tracing::{debug, info, warn};
15
16use super::functions::{evaluate_literal_as_boolean, DataStream};
17
18#[derive(Debug, Clone, Default)]
20pub struct StreamStats {
21 pub rows_processed: usize,
22 pub bytes_processed: usize,
23 pub processing_time: Duration,
24 pub spill_operations: usize,
25 pub cache_hits: usize,
26 pub cache_misses: usize,
27}
28pub struct StreamingPatternScan {
30 pattern: TriplePattern,
31 memory_monitor: Arc<MemoryMonitor>,
32 pub(super) spill_manager: Arc<Mutex<SpillManager>>,
33 config: StreamingConfig,
34 pub(super) current_batch: Vec<Solution>,
35 pub(super) batch_index: usize,
36 pub(super) total_results: usize,
37 pub(super) spilled_batches: Vec<String>,
38}
39impl StreamingPatternScan {
40 pub fn new(
41 pattern: TriplePattern,
42 memory_monitor: Arc<MemoryMonitor>,
43 spill_manager: Arc<Mutex<SpillManager>>,
44 config: StreamingConfig,
45 ) -> Result<Self> {
46 Ok(Self {
47 pattern,
48 memory_monitor,
49 spill_manager,
50 config,
51 current_batch: Vec::new(),
52 batch_index: 0,
53 total_results: 0,
54 spilled_batches: Vec::new(),
55 })
56 }
57 pub(super) fn generate_pattern_solutions(&mut self) -> Result<Vec<Solution>> {
59 let mut solutions = Vec::new();
60 let solution_count = match (
61 matches!(self.pattern.subject, Term::Variable(_)),
62 matches!(self.pattern.predicate, Term::Variable(_)),
63 matches!(self.pattern.object, Term::Variable(_)),
64 ) {
65 (true, true, true) => self.config.batch_size * 10,
66 (false, true, true) => self.config.batch_size * 5,
67 (true, false, true) => self.config.batch_size * 3,
68 (true, true, false) => self.config.batch_size * 5,
69 (false, false, true) => self.config.batch_size * 2,
70 (false, true, false) => self.config.batch_size,
71 (true, false, false) => self.config.batch_size * 2,
72 (false, false, false) => 1,
73 };
74 for i in 0..solution_count.min(self.config.batch_size) {
75 let mut binding = Binding::new();
76 for var in self.pattern.variables() {
77 let value = Term::Iri(
78 NamedNode::new(format!("http://example.org/resource_{i}"))
79 .expect("generated URL should be valid"),
80 );
81 binding.insert(var, value);
82 }
83 if !binding.is_empty() {
84 solutions.push(vec![binding]);
85 }
86 }
87 Ok(solutions)
88 }
89 pub(super) fn should_spill(&self) -> bool {
91 let current_usage = self.memory_monitor.get_current_usage();
92 let max_usage = self
93 .memory_monitor
94 .inner
95 .lock()
96 .expect("lock poisoned")
97 .max_allowed;
98 (current_usage as f64 / max_usage as f64) > self.config.spill_threshold
99 }
100 pub(super) fn spill_current_batch(&mut self) -> Result<()> {
102 if !self.current_batch.is_empty() {
103 let spill_id = self
104 .spill_manager
105 .lock()
106 .expect("lock poisoned")
107 .spill_data(&self.current_batch, SpillDataType::Solutions)?;
108 self.spilled_batches.push(spill_id);
109 self.current_batch.clear();
110 debug!("Spilled batch {} for pattern scan", self.batch_index);
111 }
112 Ok(())
113 }
114}
115pub struct BufferedPatternScan {
117 pattern: TriplePattern,
118 pub(super) batch_size: usize,
119 pub(super) solutions: Vec<Solution>,
120 pub(super) current_index: usize,
121 pub(super) exhausted: bool,
122}
123impl BufferedPatternScan {
124 pub fn new(pattern: TriplePattern, batch_size: usize) -> Result<Self> {
125 let mut scan = Self {
126 pattern,
127 batch_size,
128 solutions: Vec::new(),
129 current_index: 0,
130 exhausted: false,
131 };
132 scan.generate_all_solutions()?;
133 Ok(scan)
134 }
135 pub(super) fn generate_all_solutions(&mut self) -> Result<()> {
137 let solution_count = match (
138 matches!(self.pattern.subject, Term::Variable(_)),
139 matches!(self.pattern.predicate, Term::Variable(_)),
140 matches!(self.pattern.object, Term::Variable(_)),
141 ) {
142 (true, true, true) => 1000,
143 (false, true, true) => 100,
144 (true, false, true) => 50,
145 (true, true, false) => 100,
146 (false, false, true) => 20,
147 (false, true, false) => 10,
148 (true, false, false) => 20,
149 (false, false, false) => 1,
150 };
151 for i in 0..solution_count {
152 let mut binding = Binding::new();
153 for var in self.pattern.variables() {
154 let value = Term::Iri(
155 NamedNode::new(format!("http://example.org/item_{i}"))
156 .expect("generated URL should be valid"),
157 );
158 binding.insert(var, value);
159 }
160 if !binding.is_empty() {
161 self.solutions.push(vec![binding]);
162 }
163 }
164 Ok(())
165 }
166}
167pub struct SpillManager {
169 pub(super) spill_directory: PathBuf,
170 pub(super) active_spills: HashMap<String, SpillInfo>,
171 pub(super) spill_counter: usize,
172 pub(super) compression_enabled: bool,
173 pub(super) compression_level: u32,
174}
175impl SpillManager {
176 pub(super) fn new(spill_directory: PathBuf, compression_level: u32) -> Result<Self> {
177 std::fs::create_dir_all(&spill_directory)?;
178 Ok(Self {
179 spill_directory,
180 active_spills: HashMap::new(),
181 spill_counter: 0,
182 compression_enabled: compression_level > 0,
183 compression_level,
184 })
185 }
186 pub(super) fn spill_data<T: Serialize>(
187 &mut self,
188 data: &T,
189 data_type: SpillDataType,
190 ) -> Result<String> {
191 self.spill_counter += 1;
192 let spill_id = format!("spill_{c}", c = self.spill_counter);
193 let file_path = self.spill_directory.join(format!("{spill_id}.bin"));
194 let start_time = Instant::now();
195 let serialized = oxicode::serde::encode_to_vec(&data, oxicode::config::standard())?;
196 let original_size = serialized.len();
197 let final_data = if self.compression_enabled {
198 self.compress_data(&serialized)?
199 } else {
200 serialized
201 };
202 std::fs::write(&file_path, &final_data)?;
203 let spill_info = SpillInfo {
204 file_path: file_path.clone(),
205 original_size,
206 compressed_size: final_data.len(),
207 data_type,
208 creation_time: start_time,
209 access_count: 0,
210 };
211 self.active_spills.insert(spill_id.clone(), spill_info);
212 info!("Spilled {} bytes to {}", original_size, file_path.display());
213 Ok(spill_id)
214 }
215 pub(super) fn read_spill<T: for<'de> Deserialize<'de>>(&mut self, spill_id: &str) -> Result<T> {
216 let spill_info = self
217 .active_spills
218 .get_mut(spill_id)
219 .ok_or_else(|| anyhow!("Spill not found: {}", spill_id))?;
220 spill_info.access_count += 1;
221 let data = std::fs::read(&spill_info.file_path)?;
222 let decompressed = if self.compression_enabled {
223 self.decompress_data(&data)?
224 } else {
225 data
226 };
227 let deserialized =
228 oxicode::serde::decode_from_slice(&decompressed, oxicode::config::standard())
229 .map(|(v, _)| v)?;
230 Ok(deserialized)
231 }
232 pub(super) fn delete_spill(&mut self, spill_id: &str) -> Result<()> {
233 if let Some(spill_info) = self.active_spills.remove(spill_id) {
234 std::fs::remove_file(&spill_info.file_path)?;
235 debug!("Deleted spill file: {}", spill_info.file_path.display());
236 }
237 Ok(())
238 }
239 pub(super) fn cleanup_all(&mut self) -> Result<()> {
240 for spill_id in self.active_spills.keys().cloned().collect::<Vec<_>>() {
241 self.delete_spill(&spill_id)?;
242 }
243 Ok(())
244 }
245 pub(super) fn compress_data(&self, data: &[u8]) -> Result<Vec<u8>> {
246 let level = self.compression_level.min(9) as u8;
249 Ok(oxiarc_deflate::gzip_compress(data, level)?)
250 }
251 pub(super) fn decompress_data(&self, data: &[u8]) -> Result<Vec<u8>> {
252 Ok(oxiarc_deflate::gzip_decompress(data)?)
253 }
254}
255pub struct StreamingAggregation {
257 #[allow(dead_code)]
258 input_stream: Box<dyn DataStream>,
259 #[allow(dead_code)]
260 group_variables: Vec<Variable>,
261 #[allow(dead_code)]
262 aggregation_functions: Vec<AggregationFunction>,
263 #[allow(dead_code)]
264 partial_results: HashMap<String, AggregationState>,
265 #[allow(dead_code)]
266 memory_monitor: Arc<MemoryMonitor>,
267 #[allow(dead_code)]
268 spill_manager: Arc<Mutex<SpillManager>>,
269 #[allow(dead_code)]
270 config: StreamingConfig,
271}
272#[derive(Debug, Clone)]
274pub enum SpillDataType {
275 Solutions,
276 HashTable,
277 SortBuffer,
278 IntermediateResults,
279 Index,
280}
281pub struct MemoryMonitor {
283 pub(super) inner: Arc<Mutex<MemoryMonitorInner>>,
284}
285impl MemoryMonitor {
286 pub(super) fn new(max_allowed: usize) -> Self {
287 Self {
288 inner: Arc::new(Mutex::new(MemoryMonitorInner {
289 current_usage: 0,
290 peak_usage: 0,
291 max_allowed,
292 allocation_history: VecDeque::new(),
293 })),
294 }
295 }
296 pub(super) fn allocate(&self, size: usize, operation: &str) -> bool {
297 let mut inner = self.inner.lock().expect("lock poisoned");
298 if inner.current_usage + size > inner.max_allowed {
299 return false;
300 }
301 inner.current_usage += size;
302 inner.peak_usage = inner.peak_usage.max(inner.current_usage);
303 inner.allocation_history.push_back(MemoryAllocation {
304 timestamp: Instant::now(),
305 size,
306 operation: operation.to_string(),
307 freed: false,
308 });
309 if inner.allocation_history.len() > 10000 {
310 inner.allocation_history.pop_front();
311 }
312 true
313 }
314 pub(super) fn deallocate(&self, size: usize) {
315 let mut inner = self.inner.lock().expect("lock poisoned");
316 inner.current_usage = inner.current_usage.saturating_sub(size);
317 }
318 #[allow(dead_code)]
319 pub(super) fn should_spill(&self, threshold: f64) -> bool {
320 let inner = self.inner.lock().expect("lock poisoned");
321 inner.current_usage as f64 > inner.max_allowed as f64 * threshold
322 }
323 #[allow(dead_code)]
324 pub(super) fn get_usage_percentage(&self) -> f64 {
325 let inner = self.inner.lock().expect("lock poisoned");
326 inner.current_usage as f64 / inner.max_allowed as f64
327 }
328 pub(super) fn get_current_usage(&self) -> usize {
329 let inner = self.inner.lock().expect("lock poisoned");
330 inner.current_usage
331 }
332}
333#[derive(Debug, Clone)]
335pub struct MemoryAllocation {
336 pub timestamp: Instant,
337 pub size: usize,
338 pub operation: String,
339 pub freed: bool,
340}
341#[derive(Debug, Clone)]
343pub struct SpillInfo {
344 pub file_path: PathBuf,
345 pub original_size: usize,
346 pub compressed_size: usize,
347 pub data_type: SpillDataType,
348 pub creation_time: Instant,
349 pub access_count: usize,
350}
351pub struct StreamingUnion {
352 pub(super) left: Box<dyn DataStream>,
353 pub(super) right: Box<dyn DataStream>,
354 pub(super) left_exhausted: bool,
355}
356impl StreamingUnion {
357 pub(super) fn new(left: Box<dyn DataStream>, right: Box<dyn DataStream>) -> Self {
358 Self {
359 left,
360 right,
361 left_exhausted: false,
362 }
363 }
364}
365pub struct StreamingMinus {
366 pub(super) left: Box<dyn DataStream>,
367 pub(super) right: Box<dyn DataStream>,
368 #[allow(dead_code)]
369 memory_monitor: Arc<MemoryMonitor>,
370 #[allow(dead_code)]
371 spill_manager: Arc<Mutex<SpillManager>>,
372}
373impl StreamingMinus {
374 #[allow(dead_code)]
375 pub(super) fn new(
376 left: Box<dyn DataStream>,
377 right: Box<dyn DataStream>,
378 memory_monitor: Arc<MemoryMonitor>,
379 spill_manager: Arc<Mutex<SpillManager>>,
380 ) -> Self {
381 Self {
382 left,
383 right,
384 memory_monitor,
385 spill_manager,
386 }
387 }
388}
389impl StreamingMinus {
390 pub(super) fn solutions_compatible(&self, left: &Solution, right: &Solution) -> bool {
393 let left_binding = match left.first() {
394 Some(binding) => binding,
395 None => return false,
396 };
397 let right_binding = match right.first() {
398 Some(binding) => binding,
399 None => return false,
400 };
401 for (var, left_term) in left_binding.iter() {
402 if let Some(right_term) = right_binding.get(var) {
403 if left_term != right_term {
404 return false;
405 }
406 }
407 }
408 true
409 }
410}
411pub struct StreamingSelection {
412 pub(super) input: Box<dyn DataStream>,
413 pub(super) condition: crate::algebra::Expression,
414}
415impl StreamingSelection {
416 #[allow(dead_code)]
417 pub(super) fn new(input: Box<dyn DataStream>, condition: crate::algebra::Expression) -> Self {
418 Self { input, condition }
419 }
420}
421impl StreamingSelection {
422 pub(super) fn evaluate_condition(&self, solution: &Solution) -> Result<bool> {
424 use crate::algebra::{BinaryOperator, UnaryOperator};
425 let binding = match solution.first() {
426 Some(binding) => binding,
427 None => return Ok(false),
428 };
429 match &self.condition {
430 Expression::Variable(var) => Ok(binding.contains_key(var)),
431 Expression::Literal(literal) => evaluate_literal_as_boolean(literal),
432 Expression::Binary { op, left, right } => match op {
433 BinaryOperator::Equal => {
434 let left_val = self.evaluate_expression(left, binding)?;
435 let right_val = self.evaluate_expression(right, binding)?;
436 Ok(left_val == right_val)
437 }
438 BinaryOperator::NotEqual => {
439 let left_val = self.evaluate_expression(left, binding)?;
440 let right_val = self.evaluate_expression(right, binding)?;
441 Ok(left_val != right_val)
442 }
443 BinaryOperator::And => {
444 let left_result = self.evaluate_condition_expr(left, binding)?;
445 let right_result = self.evaluate_condition_expr(right, binding)?;
446 Ok(left_result && right_result)
447 }
448 BinaryOperator::Or => {
449 let left_result = self.evaluate_condition_expr(left, binding)?;
450 let right_result = self.evaluate_condition_expr(right, binding)?;
451 Ok(left_result || right_result)
452 }
453 _ => {
454 warn!("Unsupported binary operator in filter: {:?}", op);
455 Ok(true)
456 }
457 },
458 Expression::Unary { op, operand } => match op {
459 UnaryOperator::Not => {
460 let result = self.evaluate_condition_expr(operand, binding)?;
461 Ok(!result)
462 }
463 _ => {
464 warn!("Unsupported unary operator in filter: {:?}", op);
465 Ok(true)
466 }
467 },
468 Expression::Bound(var) => Ok(binding.contains_key(var)),
469 _ => {
470 warn!("Unsupported expression type in filter, defaulting to true");
471 Ok(true)
472 }
473 }
474 }
475 pub(super) fn evaluate_condition_expr(
477 &self,
478 expr: &Expression,
479 binding: &Binding,
480 ) -> Result<bool> {
481 let temp_solution = vec![binding.clone()];
482 let temp_filter = StreamingSelection {
483 input: Box::new(EmptyStream::new()),
484 condition: expr.clone(),
485 };
486 temp_filter.evaluate_condition(&temp_solution)
487 }
488 pub(super) fn evaluate_expression(
490 &self,
491 expr: &Expression,
492 binding: &Binding,
493 ) -> Result<Option<Term>> {
494 match expr {
495 Expression::Variable(var) => Ok(binding.get(var).cloned()),
496 Expression::Literal(literal) => Ok(Some(Term::Literal(literal.clone()))),
497 _ => Ok(None),
498 }
499 }
500}
501#[derive(Debug, Clone, Default)]
503pub struct StreamingStats {
504 pub total_memory_used: usize,
505 pub peak_memory_used: usize,
506 pub spill_operations: usize,
507 pub total_spill_size: usize,
508 pub total_execution_time: Duration,
509 pub rows_processed: usize,
510 pub cache_hit_rate: f64,
511}
512pub struct StreamingHashJoin {
514 pub(super) left_stream: Box<dyn DataStream>,
515 pub(super) right_stream: Box<dyn DataStream>,
516 join_variables: Vec<Variable>,
517 pub(super) hash_table: HashMap<String, Vec<Solution>>,
518 pub(super) memory_monitor: Arc<MemoryMonitor>,
519 spill_manager: Arc<Mutex<SpillManager>>,
520 #[allow(dead_code)]
521 config: StreamingConfig,
522 pub(super) left_exhausted: bool,
523 pub(super) current_batch: Option<Vec<Solution>>,
524 spilled_partitions: Vec<String>,
525 #[allow(dead_code)]
526 current_spill_index: usize,
527}
528impl StreamingHashJoin {
529 pub(super) fn new(
530 left: Box<dyn DataStream>,
531 right: Box<dyn DataStream>,
532 join_variables: Vec<Variable>,
533 memory_monitor: Arc<MemoryMonitor>,
534 spill_manager: Arc<Mutex<SpillManager>>,
535 config: StreamingConfig,
536 ) -> Result<Self> {
537 Ok(Self {
538 left_stream: left,
539 right_stream: right,
540 join_variables,
541 hash_table: HashMap::new(),
542 memory_monitor,
543 spill_manager,
544 config,
545 left_exhausted: false,
546 current_batch: None,
547 spilled_partitions: Vec::new(),
548 current_spill_index: 0,
549 })
550 }
551}
552impl StreamingHashJoin {
553 pub(super) fn extract_join_key(&self, solution: &Solution) -> String {
554 self.join_variables
555 .iter()
556 .map(|var| {
557 Self::get_solution_value(solution, var)
558 .map(|term| format!("{term:?}"))
559 .unwrap_or_else(|| "NULL".to_string())
560 })
561 .collect::<Vec<_>>()
562 .join("|")
563 }
564 pub(super) fn get_solution_value<'a>(
566 solution: &'a Solution,
567 var: &Variable,
568 ) -> Option<&'a Term> {
569 solution.first().and_then(|binding| binding.get(var))
570 }
571 pub(super) fn join_solutions(&self, left: &Solution, right: &Solution) -> Option<Solution> {
572 for var in &self.join_variables {
573 let left_val = Self::get_solution_value(left, var);
574 let right_val = Self::get_solution_value(right, var);
575 match (left_val, right_val) {
576 (Some(l), Some(r)) if l != r => return None,
577 _ => {}
578 }
579 }
580 let mut result_binding = Binding::new();
581 if let Some(left_binding) = left.first() {
582 for (var, term) in left_binding.iter() {
583 result_binding.insert(var.clone(), term.clone());
584 }
585 }
586 if let Some(right_binding) = right.first() {
587 for (var, term) in right_binding.iter() {
588 result_binding.insert(var.clone(), term.clone());
589 }
590 }
591 Some(vec![result_binding])
592 }
593 pub(super) fn spill_hash_table(&mut self) -> Result<()> {
595 if self.hash_table.is_empty() {
596 return Ok(());
597 }
598 let spill_id = self
599 .spill_manager
600 .lock()
601 .expect("lock poisoned")
602 .spill_data(&self.hash_table, SpillDataType::HashTable)?;
603 self.spilled_partitions.push(spill_id);
604 let total_size: usize = self
605 .hash_table
606 .iter()
607 .map(|(key, solutions)| key.len() + solutions.len() * std::mem::size_of::<Solution>())
608 .sum();
609 self.hash_table.clear();
610 self.memory_monitor.deallocate(total_size);
611 debug!("Spilled hash table partition with {} entries", total_size);
612 Ok(())
613 }
614 #[allow(dead_code)]
616 pub(super) fn load_spilled_partition(
617 &mut self,
618 spill_id: &str,
619 ) -> Result<HashMap<String, Vec<Solution>>> {
620 let partition: HashMap<String, Vec<Solution>> = self
621 .spill_manager
622 .lock()
623 .expect("lock poisoned")
624 .read_spill(spill_id)?;
625 Ok(partition)
626 }
627}
628pub struct StreamingProjection {
629 pub(super) input: Box<dyn DataStream>,
630 pub(super) variables: Vec<Variable>,
631}
632impl StreamingProjection {
633 #[allow(dead_code)]
634 pub(super) fn new(input: Box<dyn DataStream>, variables: Vec<Variable>) -> Self {
635 Self { input, variables }
636 }
637}
638pub struct MemoryMappedStream {
640 #[allow(dead_code)]
641 file_path: PathBuf,
642 #[allow(dead_code)]
643 current_position: usize,
644 #[allow(dead_code)]
645 total_size: usize,
646 #[allow(dead_code)]
647 batch_size: usize,
648 #[allow(dead_code)]
649 stats: StreamStats,
650}
651pub struct CompressedSpillStream {
653 #[allow(dead_code)]
654 file_path: PathBuf,
655 #[allow(dead_code)]
656 reader: Option<Box<dyn BufRead>>,
657 #[allow(dead_code)]
658 batch_size: usize,
659 #[allow(dead_code)]
660 stats: StreamStats,
661}
662pub(super) struct MemoryMonitorInner {
664 pub(super) current_usage: usize,
665 pub(super) peak_usage: usize,
666 pub(super) max_allowed: usize,
667 pub(super) allocation_history: VecDeque<MemoryAllocation>,
668}
669#[derive(Debug, Clone)]
671pub struct StreamingConfig {
672 pub max_memory_usage: usize,
674 pub spill_threshold: f64,
676 pub batch_size: usize,
678 pub parallel_workers: usize,
680 pub compression_level: u32,
682 pub enable_memory_mapping: bool,
684 pub io_buffer_size: usize,
686 pub adaptive_batching: bool,
688}
689pub struct StreamingSortMergeJoin {
691 pub(super) left_stream: Box<dyn DataStream>,
692 pub(super) right_stream: Box<dyn DataStream>,
693 join_variables: Vec<Variable>,
694 pub(super) left_buffer: VecDeque<Solution>,
695 pub(super) right_buffer: VecDeque<Solution>,
696 #[allow(dead_code)]
697 memory_monitor: Arc<MemoryMonitor>,
698 #[allow(dead_code)]
699 spill_manager: Arc<Mutex<SpillManager>>,
700 #[allow(dead_code)]
701 pub(super) config: StreamingConfig,
702}
703impl StreamingSortMergeJoin {
704 pub(super) fn new(
705 left: Box<dyn DataStream>,
706 right: Box<dyn DataStream>,
707 join_variables: Vec<Variable>,
708 memory_monitor: Arc<MemoryMonitor>,
709 spill_manager: Arc<Mutex<SpillManager>>,
710 config: StreamingConfig,
711 ) -> Result<Self> {
712 Ok(Self {
713 left_stream: left,
714 right_stream: right,
715 join_variables,
716 left_buffer: VecDeque::new(),
717 right_buffer: VecDeque::new(),
718 memory_monitor,
719 spill_manager,
720 config,
721 })
722 }
723}
724impl StreamingSortMergeJoin {
725 pub(super) fn refill_sorted_buffers(&mut self) -> Result<()> {
727 if self.left_buffer.is_empty() {
728 if let Some(mut batch) = self.left_stream.next_batch()? {
729 batch.sort_by(|a, b| self.compare_solution_keys(a, b));
730 self.left_buffer.extend(batch);
731 }
732 }
733 if self.right_buffer.is_empty() {
734 if let Some(mut batch) = self.right_stream.next_batch()? {
735 batch.sort_by(|a, b| self.compare_solution_keys(a, b));
736 self.right_buffer.extend(batch);
737 }
738 }
739 Ok(())
740 }
741 pub(super) fn compare_join_keys(
743 &self,
744 left: &Solution,
745 right: &Solution,
746 ) -> std::cmp::Ordering {
747 self.compare_solution_keys(left, right)
748 }
749 pub(super) fn compare_solution_keys(
751 &self,
752 left: &Solution,
753 right: &Solution,
754 ) -> std::cmp::Ordering {
755 for var in &self.join_variables {
756 let left_val = Self::get_solution_value(left, var);
757 let right_val = Self::get_solution_value(right, var);
758 match (left_val, right_val) {
759 (Some(left_term), Some(right_term)) => {
760 let cmp = format!("{left_term:?}").cmp(&format!("{right_term:?}"));
761 if cmp != std::cmp::Ordering::Equal {
762 return cmp;
763 }
764 }
765 (Some(_), None) => return std::cmp::Ordering::Greater,
766 (None, Some(_)) => return std::cmp::Ordering::Less,
767 (None, None) => continue,
768 }
769 }
770 std::cmp::Ordering::Equal
771 }
772 pub(super) fn extract_join_key(&self, solution: &Solution) -> String {
774 self.join_variables
775 .iter()
776 .map(|var| {
777 Self::get_solution_value(solution, var)
778 .map(|term| format!("{term:?}"))
779 .unwrap_or_else(|| "NULL".to_string())
780 })
781 .collect::<Vec<_>>()
782 .join("|")
783 }
784 pub(super) fn get_solution_value<'a>(
786 solution: &'a Solution,
787 var: &Variable,
788 ) -> Option<&'a Term> {
789 solution.first().and_then(|binding| binding.get(var))
790 }
791 pub(super) fn join_solutions(&self, left: &Solution, right: &Solution) -> Option<Solution> {
793 for var in &self.join_variables {
794 let left_val = Self::get_solution_value(left, var);
795 let right_val = Self::get_solution_value(right, var);
796 match (left_val, right_val) {
797 (Some(l), Some(r)) if l != r => return None,
798 _ => {}
799 }
800 }
801 let mut result_binding = Binding::new();
802 if let Some(left_binding) = left.first() {
803 for (var, term) in left_binding.iter() {
804 result_binding.insert(var.clone(), term.clone());
805 }
806 }
807 if let Some(right_binding) = right.first() {
808 for (var, term) in right_binding.iter() {
809 result_binding.insert(var.clone(), term.clone());
810 }
811 }
812 Some(vec![result_binding])
813 }
814}
815pub struct StreamingSort {
816 pub(super) input: Box<dyn DataStream>,
817 sort_variables: Vec<Variable>,
818 #[allow(dead_code)]
819 memory_monitor: Arc<MemoryMonitor>,
820 pub(super) spill_manager: Arc<Mutex<SpillManager>>,
821 pub(super) config: StreamingConfig,
822 pub(super) sorted_batches: Vec<String>,
823 pub(super) current_batch_index: usize,
824 pub(super) fully_sorted: bool,
825}
826impl StreamingSort {
827 #[allow(dead_code)]
828 pub(super) fn new(
829 input: Box<dyn DataStream>,
830 sort_variables: Vec<Variable>,
831 memory_monitor: Arc<MemoryMonitor>,
832 spill_manager: Arc<Mutex<SpillManager>>,
833 config: StreamingConfig,
834 ) -> Result<Self> {
835 Ok(Self {
836 input,
837 sort_variables,
838 memory_monitor,
839 spill_manager,
840 config,
841 sorted_batches: Vec::new(),
842 current_batch_index: 0,
843 fully_sorted: false,
844 })
845 }
846}
847impl StreamingSort {
848 pub(super) fn compare_solutions(&self, a: &Solution, b: &Solution) -> std::cmp::Ordering {
849 for var in &self.sort_variables {
850 let a_val = StreamingHashJoin::get_solution_value(a, var);
851 let b_val = StreamingHashJoin::get_solution_value(b, var);
852 match (a_val, b_val) {
853 (Some(a_term), Some(b_term)) => {
854 let cmp = format!("{a_term:?}").cmp(&format!("{b_term:?}"));
855 if cmp != std::cmp::Ordering::Equal {
856 return cmp;
857 }
858 }
859 (Some(_), None) => return std::cmp::Ordering::Greater,
860 (None, Some(_)) => return std::cmp::Ordering::Less,
861 (None, None) => continue,
862 }
863 }
864 std::cmp::Ordering::Equal
865 }
866}
867pub struct EmptyStream {
868 pub(super) exhausted: bool,
869}
870impl EmptyStream {
871 pub(super) fn new() -> Self {
872 Self { exhausted: false }
873 }
874}
875#[derive(Debug, Clone)]
877pub enum AggregationFunction {
878 Count,
879 Sum(Variable),
880 Avg(Variable),
881 Min(Variable),
882 Max(Variable),
883 GroupConcat(Variable, Option<String>),
884}
885#[derive(Debug, Clone)]
887pub struct AggregationState {
888 pub count: usize,
889 pub sum: f64,
890 pub min: Option<Term>,
891 pub max: Option<Term>,
892 pub values: Vec<Term>,
893}