1use std::{
41 cmp::Ordering,
42 collections::BinaryHeap,
43 io::{self, Cursor},
44 sync::Arc,
45};
46
47use vibesql_ast::OrderDirection;
48use vibesql_storage::Row;
49use vibesql_types::SqlValue;
50
51use super::{
52 row_serialization::{deserialize_row_with_keys, serialize_row_with_keys},
53 MemoryController, MemoryReservation, SpillFile,
54};
55
56pub type SortKey = Vec<(SqlValue, OrderDirection)>;
58
59pub type RowWithKeys = (Row, SortKey);
61
62#[derive(Debug, Clone)]
64pub struct ExternalSortConfig {
65 pub max_run_size: usize,
67
68 pub merge_fanout: usize,
70}
71
72impl Default for ExternalSortConfig {
73 fn default() -> Self {
74 Self {
75 max_run_size: 50_000, merge_fanout: 16, }
78 }
79}
80
81enum SortRun {
83 #[allow(dead_code)]
85 InMemory(Vec<RowWithKeys>),
86
87 OnDisk { file: SpillFile, row_count: usize },
89}
90
91impl SortRun {
92 fn row_count(&self) -> usize {
94 match self {
95 SortRun::InMemory(rows) => rows.len(),
96 SortRun::OnDisk { row_count, .. } => *row_count,
97 }
98 }
99}
100
101pub struct ExternalSort {
105 reservation: MemoryReservation,
107
108 config: ExternalSortConfig,
110
111 buffer: Vec<RowWithKeys>,
113
114 buffer_memory: usize,
116
117 runs: Vec<SortRun>,
119
120 comparator: SortKeyComparator,
122}
123
124#[derive(Clone)]
126struct SortKeyComparator;
127
128impl SortKeyComparator {
129 fn compare(&self, a: &SortKey, b: &SortKey) -> Ordering {
131 for ((val_a, dir), (val_b, _)) in a.iter().zip(b.iter()) {
132 let cmp = match (val_a.is_null(), val_b.is_null()) {
134 (true, true) => Ordering::Equal,
135 (true, false) => return Ordering::Greater, (false, true) => return Ordering::Less, (false, false) => {
138 let base_cmp = compare_sql_values(val_a, val_b);
140 match dir {
141 OrderDirection::Asc => base_cmp,
142 OrderDirection::Desc => base_cmp.reverse(),
143 }
144 }
145 };
146
147 if cmp != Ordering::Equal {
148 return cmp;
149 }
150 }
151 Ordering::Equal
152 }
153
154 fn compare_with_rowid(&self, a: &RowWithKeys, b: &RowWithKeys) -> Ordering {
160 let key_cmp = self.compare(&a.1, &b.1);
161 if key_cmp != Ordering::Equal {
162 return key_cmp;
163 }
164 a.0.row_id.cmp(&b.0.row_id)
166 }
167}
168
169fn compare_sql_values(a: &SqlValue, b: &SqlValue) -> Ordering {
171 use SqlValue::*;
172 match (a, b) {
173 (Integer(x), Integer(y)) => x.cmp(y),
174 (Smallint(x), Smallint(y)) => x.cmp(y),
175 (Bigint(x), Bigint(y)) => x.cmp(y),
176 (Unsigned(x), Unsigned(y)) => x.cmp(y),
177 (Float(x), Float(y)) => x.partial_cmp(y).unwrap_or(Ordering::Equal),
178 (Real(x), Real(y)) => x.partial_cmp(y).unwrap_or(Ordering::Equal),
179 (Double(x), Double(y)) | (Numeric(x), Numeric(y)) => {
180 x.partial_cmp(y).unwrap_or(Ordering::Equal)
181 }
182 (Character(x), Character(y)) | (Varchar(x), Varchar(y)) => x.cmp(y),
183 (Character(x), Varchar(y)) | (Varchar(x), Character(y)) => x.as_str().cmp(y.as_str()),
184 (Boolean(x), Boolean(y)) => x.cmp(y),
185 (Date(x), Date(y)) => x.cmp(y),
186 (Time(x), Time(y)) => x.cmp(y),
187 (Timestamp(x), Timestamp(y)) => x.cmp(y),
188 (Interval(x), Interval(y)) => x.cmp(y),
189 _ => Ordering::Equal, }
191}
192
193impl ExternalSort {
194 pub fn new(controller: &Arc<MemoryController>) -> Self {
196 Self::with_config(controller, ExternalSortConfig::default())
197 }
198
199 pub fn with_config(controller: &Arc<MemoryController>, config: ExternalSortConfig) -> Self {
201 Self {
202 reservation: controller.create_reservation(),
203 config,
204 buffer: Vec::new(),
205 buffer_memory: 0,
206 runs: Vec::new(),
207 comparator: SortKeyComparator,
208 }
209 }
210
211 pub fn add_row(&mut self, row: Row, sort_keys: SortKey) -> io::Result<()> {
215 let row_memory = row.estimated_size_bytes()
217 + sort_keys.iter().map(|(v, _)| v.estimated_size_bytes()).sum::<usize>()
218 + std::mem::size_of::<RowWithKeys>();
219
220 if !self.reservation.try_grow(row_memory) {
222 self.spill_buffer()?;
224
225 if !self.reservation.try_grow(row_memory) {
227 return Err(io::Error::new(
230 io::ErrorKind::OutOfMemory,
231 "single row exceeds available memory budget",
232 ));
233 }
234 }
235
236 self.buffer.push((row, sort_keys));
237 self.buffer_memory += row_memory;
238
239 if self.buffer.len() >= self.config.max_run_size {
241 self.spill_buffer()?;
242 }
243
244 Ok(())
245 }
246
247 fn spill_buffer(&mut self) -> io::Result<()> {
249 if self.buffer.is_empty() {
250 return Ok(());
251 }
252
253 let comparator = self.comparator.clone();
255 self.buffer.sort_by(|a, b| comparator.compare_with_rowid(a, b));
256
257 let temp_dir = self.reservation.temp_directory();
259 let mut spill_file = SpillFile::with_suffix(temp_dir, "sort_run")?;
260
261 let row_count = self.buffer.len();
263 let mut spill_buf = Vec::new();
264
265 for (row, keys) in &self.buffer {
266 serialize_row_with_keys(row, keys, &mut spill_buf)?;
267 }
268
269 spill_file.write_all(&spill_buf)?;
270 spill_file.flush()?;
271
272 self.reservation.record_spill(spill_buf.len());
274
275 self.reservation.shrink(self.buffer_memory);
277 self.buffer.clear();
278 self.buffer_memory = 0;
279
280 self.runs.push(SortRun::OnDisk { file: spill_file, row_count });
282
283 Ok(())
284 }
285
286 pub fn finish(mut self) -> io::Result<SortedIterator> {
290 if !self.buffer.is_empty() {
292 let comparator = self.comparator.clone();
293 self.buffer.sort_by(|a, b| comparator.compare_with_rowid(a, b));
294
295 if self.runs.is_empty() {
297 return Ok(SortedIterator::InMemory(InMemoryIterator::new(
299 self.buffer,
300 self.reservation,
301 )));
302 }
303
304 self.spill_buffer()?;
306 }
307
308 if self.runs.is_empty() {
310 return Ok(SortedIterator::InMemory(InMemoryIterator::new(
311 Vec::new(),
312 self.reservation,
313 )));
314 }
315
316 Ok(SortedIterator::Merge(MergeIterator::new(self.runs, self.comparator, self.reservation)?))
318 }
319
320 pub fn run_count(&self) -> usize {
322 self.runs.len() + if self.buffer.is_empty() { 0 } else { 1 }
323 }
324
325 pub fn total_rows(&self) -> usize {
327 self.runs.iter().map(|r| r.row_count()).sum::<usize>() + self.buffer.len()
328 }
329}
330
331pub enum SortedIterator {
333 InMemory(InMemoryIterator),
335
336 Merge(MergeIterator),
338}
339
340impl Iterator for SortedIterator {
341 type Item = io::Result<Row>;
342
343 fn next(&mut self) -> Option<Self::Item> {
344 match self {
345 SortedIterator::InMemory(iter) => iter.next(),
346 SortedIterator::Merge(iter) => iter.next(),
347 }
348 }
349}
350
351pub struct InMemoryIterator {
353 rows: std::vec::IntoIter<RowWithKeys>,
354 #[allow(dead_code)]
355 reservation: MemoryReservation, }
357
358impl InMemoryIterator {
359 fn new(rows: Vec<RowWithKeys>, reservation: MemoryReservation) -> Self {
360 Self { rows: rows.into_iter(), reservation }
361 }
362}
363
364impl Iterator for InMemoryIterator {
365 type Item = io::Result<Row>;
366
367 fn next(&mut self) -> Option<Self::Item> {
368 self.rows.next().map(|(row, _keys)| Ok(row))
369 }
370}
371
372pub struct MergeIterator {
374 heap: BinaryHeap<MergeEntry>,
376
377 readers: Vec<RunReader>,
379
380 comparator: SortKeyComparator,
382
383 #[allow(dead_code)]
385 reservation: MemoryReservation,
386}
387
388struct MergeEntry {
390 row: Row,
392
393 key: SortKey,
395
396 run_index: usize,
398
399 comparator: SortKeyComparator,
401}
402
403impl PartialEq for MergeEntry {
404 fn eq(&self, other: &Self) -> bool {
405 let key_cmp = self.comparator.compare(&self.key, &other.key);
407 if key_cmp != Ordering::Equal {
408 return false;
409 }
410 self.row.row_id == other.row.row_id
411 }
412}
413
414impl Eq for MergeEntry {}
415
416impl PartialOrd for MergeEntry {
417 fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
418 Some(self.cmp(other))
419 }
420}
421
422impl Ord for MergeEntry {
423 fn cmp(&self, other: &Self) -> Ordering {
424 let key_cmp = self.comparator.compare(&other.key, &self.key);
427 if key_cmp != Ordering::Equal {
428 return key_cmp;
429 }
430 other.row.row_id.cmp(&self.row.row_id)
433 }
434}
435
436struct RunReader {
438 cursor: Cursor<Vec<u8>>,
440
441 remaining: usize,
443}
444
445impl RunReader {
446 fn new(mut file: SpillFile, row_count: usize) -> io::Result<Self> {
447 file.prepare_for_read()?;
448 let data = file.read_to_vec()?;
449 Ok(Self { cursor: Cursor::new(data), remaining: row_count })
450 }
451
452 fn read_next(&mut self) -> io::Result<Option<RowWithKeys>> {
453 if self.remaining == 0 {
454 return Ok(None);
455 }
456 self.remaining -= 1;
457 let (row, keys) = deserialize_row_with_keys(&mut self.cursor)?;
458 Ok(Some((row, keys)))
459 }
460}
461
462impl MergeIterator {
463 fn new(
464 runs: Vec<SortRun>,
465 comparator: SortKeyComparator,
466 reservation: MemoryReservation,
467 ) -> io::Result<Self> {
468 let mut readers = Vec::with_capacity(runs.len());
469 let mut heap = BinaryHeap::new();
470
471 for (run_index, run) in runs.into_iter().enumerate() {
472 match run {
473 SortRun::InMemory(rows) => {
474 for (row, key) in rows {
476 heap.push(MergeEntry {
477 row,
478 key,
479 run_index,
480 comparator: comparator.clone(),
481 });
482 }
483 readers.push(RunReader { cursor: Cursor::new(Vec::new()), remaining: 0 });
484 }
485 SortRun::OnDisk { file, row_count } => {
486 let mut reader = RunReader::new(file, row_count)?;
487
488 if let Some((row, key)) = reader.read_next()? {
490 heap.push(MergeEntry {
491 row,
492 key,
493 run_index,
494 comparator: comparator.clone(),
495 });
496 }
497
498 readers.push(reader);
499 }
500 }
501 }
502
503 Ok(Self { heap, readers, comparator, reservation })
504 }
505}
506
507impl Iterator for MergeIterator {
508 type Item = io::Result<Row>;
509
510 fn next(&mut self) -> Option<Self::Item> {
511 let entry = self.heap.pop()?;
513
514 match self.readers[entry.run_index].read_next() {
516 Ok(Some((row, key))) => {
517 self.heap.push(MergeEntry {
518 row,
519 key,
520 run_index: entry.run_index,
521 comparator: self.comparator.clone(),
522 });
523 }
524 Ok(None) => {
525 }
527 Err(e) => {
528 return Some(Err(e));
529 }
530 }
531
532 Some(Ok(entry.row))
533 }
534}
535
536#[cfg(test)]
537mod tests {
538 use super::*;
539
540 fn make_test_controller() -> Arc<MemoryController> {
541 Arc::new(MemoryController::with_budget(1024 * 1024)) }
543
544 #[test]
545 fn test_in_memory_sort() {
546 let controller = make_test_controller();
547 let mut sorter = ExternalSort::new(&controller);
548
549 for i in (0..100).rev() {
551 let row = Row::from_vec(vec![SqlValue::Integer(i)]);
552 let keys = vec![(SqlValue::Integer(i), OrderDirection::Asc)];
553 sorter.add_row(row, keys).unwrap();
554 }
555
556 let results: Vec<Row> = sorter.finish().unwrap().map(|r| r.unwrap()).collect();
558
559 assert_eq!(results.len(), 100);
560 for (i, row) in results.iter().enumerate() {
561 assert_eq!(row.values[0], SqlValue::Integer(i as i64));
562 }
563 }
564
565 #[test]
566 fn test_descending_sort() {
567 let controller = make_test_controller();
568 let mut sorter = ExternalSort::new(&controller);
569
570 for i in 0..50 {
572 let row = Row::from_vec(vec![SqlValue::Integer(i)]);
573 let keys = vec![(SqlValue::Integer(i), OrderDirection::Desc)];
574 sorter.add_row(row, keys).unwrap();
575 }
576
577 let results: Vec<Row> = sorter.finish().unwrap().map(|r| r.unwrap()).collect();
578
579 assert_eq!(results.len(), 50);
580 for (i, row) in results.iter().enumerate() {
582 assert_eq!(row.values[0], SqlValue::Integer(49 - i as i64));
583 }
584 }
585
586 #[test]
587 fn test_null_handling() {
588 let controller = make_test_controller();
589 let mut sorter = ExternalSort::new(&controller);
590
591 sorter
593 .add_row(
594 Row::from_vec(vec![SqlValue::Integer(2)]),
595 vec![(SqlValue::Integer(2), OrderDirection::Asc)],
596 )
597 .unwrap();
598 sorter
599 .add_row(
600 Row::from_vec(vec![SqlValue::Null]),
601 vec![(SqlValue::Null, OrderDirection::Asc)],
602 )
603 .unwrap();
604 sorter
605 .add_row(
606 Row::from_vec(vec![SqlValue::Integer(1)]),
607 vec![(SqlValue::Integer(1), OrderDirection::Asc)],
608 )
609 .unwrap();
610
611 let results: Vec<Row> = sorter.finish().unwrap().map(|r| r.unwrap()).collect();
612
613 assert_eq!(results[0].values[0], SqlValue::Integer(1));
615 assert_eq!(results[1].values[0], SqlValue::Integer(2));
616 assert_eq!(results[2].values[0], SqlValue::Null);
617 }
618
619 #[test]
620 fn test_multi_key_sort() {
621 let controller = make_test_controller();
622 let mut sorter = ExternalSort::new(&controller);
623
624 sorter
627 .add_row(
628 Row::from_vec(vec![SqlValue::Integer(1), SqlValue::Integer(2)]),
629 vec![
630 (SqlValue::Integer(1), OrderDirection::Asc),
631 (SqlValue::Integer(2), OrderDirection::Asc),
632 ],
633 )
634 .unwrap();
635 sorter
636 .add_row(
637 Row::from_vec(vec![SqlValue::Integer(1), SqlValue::Integer(1)]),
638 vec![
639 (SqlValue::Integer(1), OrderDirection::Asc),
640 (SqlValue::Integer(1), OrderDirection::Asc),
641 ],
642 )
643 .unwrap();
644 sorter
645 .add_row(
646 Row::from_vec(vec![SqlValue::Integer(2), SqlValue::Integer(1)]),
647 vec![
648 (SqlValue::Integer(2), OrderDirection::Asc),
649 (SqlValue::Integer(1), OrderDirection::Asc),
650 ],
651 )
652 .unwrap();
653
654 let results: Vec<Row> = sorter.finish().unwrap().map(|r| r.unwrap()).collect();
655
656 assert_eq!(results[0].values[0], SqlValue::Integer(1));
658 assert_eq!(results[0].values[1], SqlValue::Integer(1));
659 assert_eq!(results[1].values[0], SqlValue::Integer(1));
660 assert_eq!(results[1].values[1], SqlValue::Integer(2));
661 assert_eq!(results[2].values[0], SqlValue::Integer(2));
662 assert_eq!(results[2].values[1], SqlValue::Integer(1));
663 }
664
665 #[test]
666 fn test_spill_and_merge() {
667 let controller = Arc::new(MemoryController::with_budget(4096)); let config = ExternalSortConfig {
670 max_run_size: 10, ..Default::default()
672 };
673
674 let mut sorter = ExternalSort::with_config(&controller, config);
675
676 for i in (0..50).rev() {
678 let row = Row::from_vec(vec![SqlValue::Integer(i)]);
679 let keys = vec![(SqlValue::Integer(i), OrderDirection::Asc)];
680 sorter.add_row(row, keys).unwrap();
681 }
682
683 assert!(sorter.run_count() > 1, "Expected multiple runs from spilling");
685
686 let results: Vec<Row> = sorter.finish().unwrap().map(|r| r.unwrap()).collect();
688
689 assert_eq!(results.len(), 50);
690 for (i, row) in results.iter().enumerate() {
691 assert_eq!(row.values[0], SqlValue::Integer(i as i64));
692 }
693 }
694
695 #[test]
696 fn test_empty_sort() {
697 let controller = make_test_controller();
698 let sorter = ExternalSort::new(&controller);
699
700 let results: Vec<Row> = sorter.finish().unwrap().map(|r| r.unwrap()).collect();
701 assert!(results.is_empty());
702 }
703
704 #[test]
705 fn test_string_sort() {
706 let controller = make_test_controller();
707 let mut sorter = ExternalSort::new(&controller);
708
709 sorter
710 .add_row(
711 Row::from_vec(vec![SqlValue::Varchar("charlie".into())]),
712 vec![(SqlValue::Varchar("charlie".into()), OrderDirection::Asc)],
713 )
714 .unwrap();
715 sorter
716 .add_row(
717 Row::from_vec(vec![SqlValue::Varchar("alpha".into())]),
718 vec![(SqlValue::Varchar("alpha".into()), OrderDirection::Asc)],
719 )
720 .unwrap();
721 sorter
722 .add_row(
723 Row::from_vec(vec![SqlValue::Varchar("bravo".into())]),
724 vec![(SqlValue::Varchar("bravo".into()), OrderDirection::Asc)],
725 )
726 .unwrap();
727
728 let results: Vec<Row> = sorter.finish().unwrap().map(|r| r.unwrap()).collect();
729
730 assert_eq!(results[0].values[0], SqlValue::Varchar("alpha".into()));
731 assert_eq!(results[1].values[0], SqlValue::Varchar("bravo".into()));
732 assert_eq!(results[2].values[0], SqlValue::Varchar("charlie".into()));
733 }
734}