1use std::cmp::Ordering;
18use std::marker::PhantomData;
19use std::path::PathBuf;
20
21use crate::catalog::{ColumnMetadata, TableMetadata};
22use crate::executor::evaluator::EvalContext;
23use crate::executor::memory::{MemoryPolicy, MemoryTracker, map_core_memory_error};
24use crate::executor::{ExecutorError, Result, Row};
25use crate::planner::typed_expr::{SortExpr, TypedExpr};
26use crate::storage::{RowCodec, SqlValue, TableScanIterator};
27use alopex_core::Error as CoreError;
28use alopex_core::sql::spill::{
29 SpillMergeIterator, spill_io_error as core_spill_io_error, spill_run as core_spill_run,
30};
31
32pub trait RowIterator {
37 fn next_row(&mut self) -> Option<Result<Row>>;
44
45 fn schema(&self) -> &[ColumnMetadata];
47}
48
49impl RowIterator for Box<dyn RowIterator + '_> {
51 fn next_row(&mut self) -> Option<Result<Row>> {
52 (**self).next_row()
53 }
54
55 fn schema(&self) -> &[ColumnMetadata] {
56 (**self).schema()
57 }
58}
59
60pub struct ScanIterator<'a> {
69 inner: TableScanIterator<'a>,
70 schema: Vec<ColumnMetadata>,
71}
72
73impl<'a> ScanIterator<'a> {
74 pub fn new(inner: TableScanIterator<'a>, table_meta: &TableMetadata) -> Self {
76 Self {
77 inner,
78 schema: table_meta.columns.clone(),
79 }
80 }
81}
82
83impl RowIterator for ScanIterator<'_> {
84 fn next_row(&mut self) -> Option<Result<Row>> {
85 self.inner.next().map(|result| {
86 result
87 .map(|(row_id, values)| Row::new(row_id, values))
88 .map_err(ExecutorError::from)
89 })
90 }
91
92 fn schema(&self) -> &[ColumnMetadata] {
93 &self.schema
94 }
95}
96
97pub struct FilterIterator<I: RowIterator> {
106 input: I,
107 predicate: TypedExpr,
108}
109
110impl<I: RowIterator> FilterIterator<I> {
111 pub fn new(input: I, predicate: TypedExpr) -> Self {
113 Self { input, predicate }
114 }
115}
116
117impl<I: RowIterator> RowIterator for FilterIterator<I> {
118 fn next_row(&mut self) -> Option<Result<Row>> {
119 loop {
120 match self.input.next_row()? {
121 Ok(row) => {
122 let ctx = EvalContext::new(&row.values);
123 match crate::executor::evaluator::evaluate(&self.predicate, &ctx) {
124 Ok(SqlValue::Boolean(true)) => return Some(Ok(row)),
125 Ok(_) => continue, Err(e) => return Some(Err(e)),
127 }
128 }
129 Err(e) => return Some(Err(e)),
130 }
131 }
132 }
133
134 fn schema(&self) -> &[ColumnMetadata] {
135 self.input.schema()
136 }
137}
138
139pub struct SortIterator<I: RowIterator> {
149 output: SortOutput,
150 schema: Vec<ColumnMetadata>,
152 _marker: PhantomData<I>,
154}
155
156enum SortOutput {
157 InMemory(std::vec::IntoIter<Row>),
158 External(ExternalSortState),
159}
160
161impl<I: RowIterator> SortIterator<I> {
162 pub fn new(input: I, order_by: &[SortExpr]) -> Result<Self> {
170 Self::new_with_policy(input, order_by, None)
171 }
172
173 pub fn new_with_policy(
175 mut input: I,
176 order_by: &[SortExpr],
177 policy: Option<MemoryPolicy>,
178 ) -> Result<Self> {
179 let schema = input.schema().to_vec();
180 let mut tracker = policy.clone().map(MemoryTracker::new);
181
182 if order_by.is_empty() {
183 let mut rows = Vec::new();
184 while let Some(result) = input.next_row() {
185 rows.push(result?);
186 if let Some(tracker) = &mut tracker {
187 let row = rows.last().expect("row just pushed");
188 tracker
189 .add_row(&row.values)
190 .map_err(map_core_memory_error)?;
191 }
192 }
193 return Ok(Self {
194 output: SortOutput::InMemory(rows.into_iter()),
195 schema,
196 _marker: PhantomData,
197 });
198 }
199
200 let allow_spill = policy
201 .as_ref()
202 .and_then(|policy| policy.spill_directory())
203 .is_some();
204 let mut runs: Vec<PathBuf> = Vec::new();
205 let mut keyed: Vec<(Row, Vec<SqlValue>)> = Vec::new();
206
207 while let Some(result) = input.next_row() {
208 let row = result?;
209 let mut keys = Vec::with_capacity(order_by.len());
210 for expr in order_by {
211 let ctx = EvalContext::new(&row.values);
212 keys.push(crate::executor::evaluator::evaluate(&expr.expr, &ctx)?);
213 }
214 if let Some(tracker) = &mut tracker {
215 tracker
216 .add_row(&row.values)
217 .map_err(map_core_memory_error)?;
218 tracker.add_values(&keys).map_err(map_core_memory_error)?;
219 }
220 keyed.push((row, keys));
221
222 if allow_spill && tracker.as_ref().map(|t| t.over_limit()).unwrap_or(false) {
223 let policy = policy
224 .as_ref()
225 .ok_or_else(|| ExecutorError::InvalidOperation {
226 operation: "sort spill".into(),
227 reason: "spill policy missing".into(),
228 })?;
229 let path = spill_run(&mut keyed, order_by, policy)?;
230 runs.push(path);
231 if let Some(tracker) = &mut tracker {
232 tracker.reset();
233 }
234 }
235 }
236
237 if runs.is_empty() {
238 keyed.sort_by(|a, b| compare_key_values(&a.1, &b.1, order_by));
239 let sorted: Vec<Row> = keyed.into_iter().map(|(row, _)| row).collect();
240 return Ok(Self {
241 output: SortOutput::InMemory(sorted.into_iter()),
242 schema,
243 _marker: PhantomData,
244 });
245 }
246
247 if !keyed.is_empty() {
248 let policy = policy
249 .as_ref()
250 .ok_or_else(|| ExecutorError::InvalidOperation {
251 operation: "sort spill".into(),
252 reason: "spill policy missing".into(),
253 })?;
254 let path = spill_run(&mut keyed, order_by, policy)?;
255 runs.push(path);
256 }
257
258 let external = ExternalSortState::new(order_by.to_vec(), runs)?;
259
260 Ok(Self {
261 output: SortOutput::External(external),
262 schema,
263 _marker: PhantomData,
264 })
265 }
266}
267
268impl<I: RowIterator> RowIterator for SortIterator<I> {
269 fn next_row(&mut self) -> Option<Result<Row>> {
270 match &mut self.output {
271 SortOutput::InMemory(iter) => iter.next().map(Ok),
272 SortOutput::External(state) => state.next_row(),
273 }
274 }
275
276 fn schema(&self) -> &[ColumnMetadata] {
277 &self.schema
278 }
279}
280
281fn spill_run(
282 entries: &mut Vec<(Row, Vec<SqlValue>)>,
283 order_by: &[SortExpr],
284 policy: &MemoryPolicy,
285) -> Result<PathBuf> {
286 core_spill_run(
287 entries,
288 policy,
289 "sort-run",
290 |left, right| compare_key_values(left, right, order_by),
291 |row| row.row_id,
292 |keys| RowCodec::encode(keys),
293 |row| RowCodec::encode(&row.values),
294 )
295 .map_err(map_core_spill_error)
296}
297
298struct ExternalSortState {
299 iter: SpillMergeIterator<Row, Vec<SqlValue>>,
300}
301
302impl ExternalSortState {
303 fn new(order_by: Vec<SortExpr>, runs: Vec<PathBuf>) -> Result<Self> {
304 let iter = SpillMergeIterator::new(
305 runs,
306 move |left, right| compare_key_values(left, right, &order_by),
307 |row: &Row| row.row_id,
308 decode_spill_values,
309 |row_id, bytes| decode_spill_values(bytes).map(|values| Row::new(row_id, values)),
310 )
311 .map_err(map_core_spill_error)?;
312
313 Ok(Self { iter })
314 }
315
316 fn next_row(&mut self) -> Option<Result<Row>> {
317 self.iter
318 .next_item()
319 .map(|result| result.map_err(map_core_spill_error))
320 }
321}
322
323fn decode_spill_values(bytes: &[u8]) -> alopex_core::Result<Vec<SqlValue>> {
324 RowCodec::decode(bytes).map_err(|err| CoreError::SpillFailed {
325 reason: format!("sort spill: {err}"),
326 })
327}
328
329fn map_core_spill_error(err: CoreError) -> ExecutorError {
330 match err {
331 CoreError::SpillFailed { reason } => ExecutorError::InvalidOperation {
332 operation: "sort spill".into(),
333 reason,
334 },
335 CoreError::Io(err) => ExecutorError::InvalidOperation {
336 operation: "sort spill".into(),
337 reason: core_spill_io_error("sort spill", err).to_string(),
338 },
339 CoreError::MemoryLimitExceeded { limit, requested } => ExecutorError::ResourceExhausted {
340 message: format!("query memory limit exceeded: {requested} bytes (limit {limit})"),
341 },
342 other => ExecutorError::Core(other),
343 }
344}
345
346pub(super) fn compare_key_values(
347 a: &[SqlValue],
348 b: &[SqlValue],
349 order_by: &[SortExpr],
350) -> Ordering {
351 for (i, sort_expr) in order_by.iter().enumerate() {
352 let left = &a[i];
353 let right = &b[i];
354 let cmp = compare_single(left, right, sort_expr.asc, sort_expr.nulls_first);
355 if cmp != Ordering::Equal {
356 return cmp;
357 }
358 }
359 Ordering::Equal
360}
361
362pub(super) fn compare_single(
364 left: &SqlValue,
365 right: &SqlValue,
366 asc: bool,
367 nulls_first: bool,
368) -> Ordering {
369 match (left, right) {
370 (SqlValue::Null, SqlValue::Null) => Ordering::Equal,
371 (SqlValue::Null, _) => {
372 if nulls_first {
373 Ordering::Less
374 } else {
375 Ordering::Greater
376 }
377 }
378 (_, SqlValue::Null) => {
379 if nulls_first {
380 Ordering::Greater
381 } else {
382 Ordering::Less
383 }
384 }
385 _ => match compare_non_null_values(left, right) {
386 Ordering::Equal => Ordering::Equal,
387 ord if asc => ord,
388 ord => ord.reverse(),
389 },
390 }
391}
392
393fn compare_non_null_values(left: &SqlValue, right: &SqlValue) -> Ordering {
394 if let Some(ordering) = left.partial_cmp(right) {
395 return ordering;
396 }
397 match (left, right) {
398 (SqlValue::Float(left), SqlValue::Float(right)) => compare_f32(*left, *right),
399 (SqlValue::Double(left), SqlValue::Double(right)) => compare_f64(*left, *right),
400 (SqlValue::Vector(left), SqlValue::Vector(right)) => compare_vectors(left, right),
401 _ => left.type_tag().cmp(&right.type_tag()),
404 }
405}
406
407fn compare_f32(left: f32, right: f32) -> Ordering {
408 if left.partial_cmp(&right) == Some(Ordering::Equal) || (left.is_nan() && right.is_nan()) {
409 Ordering::Equal
410 } else if left.is_nan() {
411 Ordering::Greater
412 } else if right.is_nan() {
413 Ordering::Less
414 } else {
415 left.partial_cmp(&right)
416 .expect("non-NaN f32 values are totally ordered")
417 }
418}
419
420fn compare_f64(left: f64, right: f64) -> Ordering {
421 if left.partial_cmp(&right) == Some(Ordering::Equal) || (left.is_nan() && right.is_nan()) {
422 Ordering::Equal
423 } else if left.is_nan() {
424 Ordering::Greater
425 } else if right.is_nan() {
426 Ordering::Less
427 } else {
428 left.partial_cmp(&right)
429 .expect("non-NaN f64 values are totally ordered")
430 }
431}
432
433fn compare_vectors(left: &[f32], right: &[f32]) -> Ordering {
434 for (left, right) in left.iter().zip(right) {
435 let ordering = compare_f32(*left, *right);
436 if ordering != Ordering::Equal {
437 return ordering;
438 }
439 }
440 left.len().cmp(&right.len())
441}
442
443pub struct DistinctOnIterator<I: RowIterator> {
457 input: SortIterator<I>,
458 key_exprs: Vec<SortExpr>,
459 last_keys: Option<Vec<SqlValue>>,
460}
461
462impl<I: RowIterator> DistinctOnIterator<I> {
463 pub fn new(
472 input: I,
473 order_by: &[SortExpr],
474 key_count: usize,
475 policy: Option<MemoryPolicy>,
476 ) -> Result<Self> {
477 debug_assert!((1..=order_by.len()).contains(&key_count));
478 let sorted = SortIterator::new_with_policy(input, order_by, policy)?;
479 Ok(Self {
480 input: sorted,
481 key_exprs: order_by[..key_count.min(order_by.len())].to_vec(),
482 last_keys: None,
483 })
484 }
485}
486
487impl<I: RowIterator> RowIterator for DistinctOnIterator<I> {
488 fn next_row(&mut self) -> Option<Result<Row>> {
489 loop {
490 let row = match self.input.next_row()? {
491 Ok(row) => row,
492 Err(err) => return Some(Err(err)),
493 };
494 let mut keys = Vec::with_capacity(self.key_exprs.len());
495 for sort in &self.key_exprs {
496 let ctx = EvalContext::new(&row.values);
497 match crate::executor::evaluator::evaluate(&sort.expr, &ctx) {
498 Ok(value) => keys.push(value),
499 Err(err) => return Some(Err(err)),
500 }
501 }
502 let starts_new_group = match &self.last_keys {
503 None => true,
504 Some(previous) => previous.iter().zip(&keys).any(|(left, right)| {
505 compare_single(left, right, true, false) != Ordering::Equal
509 }),
510 };
511 if starts_new_group {
512 self.last_keys = Some(keys);
513 return Some(Ok(row));
514 }
515 }
516 }
517
518 fn schema(&self) -> &[ColumnMetadata] {
519 self.input.schema()
520 }
521}
522
523pub struct LimitIterator<I: RowIterator> {
538 input: I,
539 limit: Option<u64>,
540 offset: u64,
541 skipped: u64,
543 yielded: u64,
545 tie_keys: Vec<SortExpr>,
547 boundary: Option<Vec<SqlValue>>,
549 done: bool,
551}
552
553impl<I: RowIterator> LimitIterator<I> {
554 pub fn new(input: I, limit: Option<u64>, offset: Option<u64>) -> Self {
556 Self::with_ties(input, limit, offset, Vec::new())
557 }
558
559 pub fn with_ties(
564 input: I,
565 limit: Option<u64>,
566 offset: Option<u64>,
567 tie_keys: Vec<SortExpr>,
568 ) -> Self {
569 Self {
570 input,
571 limit,
572 offset: offset.unwrap_or(0),
573 skipped: 0,
574 yielded: 0,
575 tie_keys,
576 boundary: None,
577 done: false,
578 }
579 }
580
581 fn row_keys(&self, row: &Row) -> Result<Vec<SqlValue>> {
582 let ctx = EvalContext::new(&row.values);
583 self.tie_keys
584 .iter()
585 .map(|sort| crate::executor::evaluator::evaluate(&sort.expr, &ctx))
586 .collect()
587 }
588}
589
590impl<I: RowIterator> RowIterator for LimitIterator<I> {
591 fn next_row(&mut self) -> Option<Result<Row>> {
592 if self.done {
593 return None;
594 }
595
596 if let Some(limit) = self.limit
598 && self.yielded >= limit
599 {
600 if self.tie_keys.is_empty() {
601 return None;
602 }
603 let Some(boundary) = self.boundary.clone() else {
606 self.done = true;
607 return None;
608 };
609 return match self.input.next_row() {
610 Some(Ok(row)) => {
611 let keys = match self.row_keys(&row) {
612 Ok(keys) => keys,
613 Err(error) => return Some(Err(error)),
614 };
615 if compare_key_values(&keys, &boundary, &self.tie_keys) == Ordering::Equal {
616 Some(Ok(row))
617 } else {
618 self.done = true;
619 None
620 }
621 }
622 Some(Err(error)) => Some(Err(error)),
623 None => {
624 self.done = true;
625 None
626 }
627 };
628 }
629
630 loop {
631 match self.input.next_row()? {
632 Ok(row) => {
633 if self.skipped < self.offset {
635 self.skipped += 1;
636 continue;
637 }
638
639 if let Some(limit) = self.limit
641 && self.yielded >= limit
642 {
643 return None;
644 }
645
646 if !self.tie_keys.is_empty() {
647 match self.row_keys(&row) {
648 Ok(keys) => self.boundary = Some(keys),
649 Err(error) => return Some(Err(error)),
650 }
651 }
652 self.yielded += 1;
653 return Some(Ok(row));
654 }
655 Err(e) => return Some(Err(e)),
656 }
657 }
658 }
659
660 fn schema(&self) -> &[ColumnMetadata] {
661 self.input.schema()
662 }
663}
664
665pub struct VecIterator {
674 rows: std::vec::IntoIter<Row>,
675 schema: Vec<ColumnMetadata>,
676}
677
678impl VecIterator {
679 pub fn new(rows: Vec<Row>, schema: Vec<ColumnMetadata>) -> Self {
681 Self {
682 rows: rows.into_iter(),
683 schema,
684 }
685 }
686}
687
688impl RowIterator for VecIterator {
689 fn next_row(&mut self) -> Option<Result<Row>> {
690 self.rows.next().map(Ok)
691 }
692
693 fn schema(&self) -> &[ColumnMetadata] {
694 &self.schema
695 }
696}
697
698pub struct ValuesIterator {
704 rows: std::vec::IntoIter<Vec<TypedExpr>>,
705 schema: Vec<ColumnMetadata>,
706 context_values: Vec<SqlValue>,
707 next_row_id: u64,
708}
709
710impl ValuesIterator {
711 pub fn new(
716 rows: Vec<Vec<TypedExpr>>,
717 schema: Vec<ColumnMetadata>,
718 outer: Option<&Row>,
719 ) -> Self {
720 Self {
721 rows: rows.into_iter(),
722 schema,
723 context_values: outer.map(|row| row.values.clone()).unwrap_or_default(),
724 next_row_id: 0,
725 }
726 }
727}
728
729impl RowIterator for ValuesIterator {
730 fn next_row(&mut self) -> Option<Result<Row>> {
731 let expressions = self.rows.next()?;
732 let context = EvalContext::new(&self.context_values);
733 let values = expressions
734 .iter()
735 .map(|expression| crate::executor::evaluator::evaluate(expression, &context))
736 .collect::<Result<Vec<_>>>();
737 let row_id = self.next_row_id;
738 self.next_row_id = self.next_row_id.saturating_add(1);
739 Some(values.map(|values| Row::new(row_id, values)))
740 }
741
742 fn schema(&self) -> &[ColumnMetadata] {
743 &self.schema
744 }
745}
746
747#[cfg(test)]
752mod tests {
753 use super::*;
754 use crate::Span;
755 use crate::planner::types::ResolvedType;
756
757 fn sample_schema() -> Vec<ColumnMetadata> {
758 vec![
759 ColumnMetadata::new("id", ResolvedType::Integer),
760 ColumnMetadata::new("name", ResolvedType::Text),
761 ]
762 }
763
764 fn sample_rows() -> Vec<Row> {
765 vec![
766 Row::new(
767 1,
768 vec![SqlValue::Integer(1), SqlValue::Text("alice".into())],
769 ),
770 Row::new(2, vec![SqlValue::Integer(2), SqlValue::Text("bob".into())]),
771 Row::new(
772 3,
773 vec![SqlValue::Integer(3), SqlValue::Text("carol".into())],
774 ),
775 Row::new(4, vec![SqlValue::Integer(4), SqlValue::Text("dave".into())]),
776 Row::new(5, vec![SqlValue::Integer(5), SqlValue::Text("eve".into())]),
777 ]
778 }
779
780 #[test]
781 fn vec_iterator_returns_all_rows() {
782 let rows = sample_rows();
783 let expected_len = rows.len();
784 let mut iter = VecIterator::new(rows, sample_schema());
785
786 let mut count = 0;
787 while let Some(Ok(_)) = iter.next_row() {
788 count += 1;
789 }
790 assert_eq!(count, expected_len);
791 }
792
793 #[test]
794 fn filter_iterator_filters_rows() {
795 use crate::ast::expr::BinaryOp;
796 use crate::planner::typed_expr::{TypedExpr, TypedExprKind};
797
798 let rows = sample_rows();
799 let schema = sample_schema();
800 let input = VecIterator::new(rows, schema);
801
802 let predicate = TypedExpr {
804 kind: TypedExprKind::BinaryOp {
805 left: Box::new(TypedExpr {
806 kind: TypedExprKind::ColumnRef {
807 table: "test".into(),
808 column: "id".into(),
809 column_index: 0,
810 },
811 resolved_type: ResolvedType::Integer,
812 span: Span::default(),
813 }),
814 op: BinaryOp::Gt,
815 right: Box::new(TypedExpr::literal(
816 crate::ast::expr::Literal::Number("2".into()),
817 ResolvedType::Integer,
818 Span::default(),
819 )),
820 },
821 resolved_type: ResolvedType::Boolean,
822 span: Span::default(),
823 };
824
825 let mut filter = FilterIterator::new(input, predicate);
826
827 let mut results = Vec::new();
828 while let Some(Ok(row)) = filter.next_row() {
829 results.push(row);
830 }
831
832 assert_eq!(results.len(), 3);
833 assert_eq!(results[0].row_id, 3);
834 assert_eq!(results[1].row_id, 4);
835 assert_eq!(results[2].row_id, 5);
836 }
837
838 #[test]
839 fn limit_iterator_limits_rows() {
840 let rows = sample_rows();
841 let schema = sample_schema();
842 let input = VecIterator::new(rows, schema);
843
844 let mut limit = LimitIterator::new(input, Some(2), None);
845
846 let mut results = Vec::new();
847 while let Some(Ok(row)) = limit.next_row() {
848 results.push(row);
849 }
850
851 assert_eq!(results.len(), 2);
852 assert_eq!(results[0].row_id, 1);
853 assert_eq!(results[1].row_id, 2);
854 }
855
856 #[test]
857 fn limit_iterator_applies_offset() {
858 let rows = sample_rows();
859 let schema = sample_schema();
860 let input = VecIterator::new(rows, schema);
861
862 let mut limit = LimitIterator::new(input, Some(2), Some(2));
863
864 let mut results = Vec::new();
865 while let Some(Ok(row)) = limit.next_row() {
866 results.push(row);
867 }
868
869 assert_eq!(results.len(), 2);
870 assert_eq!(results[0].row_id, 3);
871 assert_eq!(results[1].row_id, 4);
872 }
873
874 #[test]
875 fn limit_iterator_offset_only() {
876 let rows = sample_rows();
877 let schema = sample_schema();
878 let input = VecIterator::new(rows, schema);
879
880 let mut limit = LimitIterator::new(input, None, Some(3));
881
882 let mut results = Vec::new();
883 while let Some(Ok(row)) = limit.next_row() {
884 results.push(row);
885 }
886
887 assert_eq!(results.len(), 2);
888 assert_eq!(results[0].row_id, 4);
889 assert_eq!(results[1].row_id, 5);
890 }
891
892 fn score_sort_key() -> SortExpr {
893 use crate::planner::typed_expr::{TypedExpr, TypedExprKind};
894 SortExpr {
895 expr: TypedExpr {
896 kind: TypedExprKind::ColumnRef {
897 table: "test".into(),
898 column: "id".into(),
899 column_index: 0,
900 },
901 resolved_type: ResolvedType::Integer,
902 span: Span::default(),
903 },
904 asc: true,
905 nulls_first: false,
906 }
907 }
908
909 #[test]
910 fn limit_iterator_with_ties_keeps_boundary_peers() {
911 let rows = vec![
913 Row::new(1, vec![SqlValue::Integer(10)]),
914 Row::new(2, vec![SqlValue::Integer(20)]),
915 Row::new(3, vec![SqlValue::Integer(20)]),
916 Row::new(4, vec![SqlValue::Integer(20)]),
917 Row::new(5, vec![SqlValue::Integer(30)]),
918 ];
919 let schema = vec![ColumnMetadata::new("id", ResolvedType::Integer)];
920 let input = VecIterator::new(rows, schema);
921 let mut limit = LimitIterator::with_ties(input, Some(2), None, vec![score_sort_key()]);
922
923 let mut results = Vec::new();
924 while let Some(Ok(row)) = limit.next_row() {
925 results.push(row.row_id);
926 }
927 assert_eq!(results, vec![1, 2, 3, 4]);
928 }
929
930 #[test]
931 fn limit_iterator_with_ties_stops_on_the_first_non_peer() {
932 let rows = vec![
933 Row::new(1, vec![SqlValue::Integer(10)]),
934 Row::new(2, vec![SqlValue::Integer(20)]),
935 Row::new(3, vec![SqlValue::Integer(30)]),
936 ];
937 let schema = vec![ColumnMetadata::new("id", ResolvedType::Integer)];
938 let input = VecIterator::new(rows, schema);
939 let mut limit = LimitIterator::with_ties(input, Some(2), None, vec![score_sort_key()]);
940
941 let mut results = Vec::new();
942 while let Some(Ok(row)) = limit.next_row() {
943 results.push(row.row_id);
944 }
945 assert_eq!(results, vec![1, 2]);
946 assert!(limit.next_row().is_none());
948 }
949
950 #[test]
951 fn limit_iterator_with_ties_null_keys_are_peers() {
952 let rows = vec![
953 Row::new(1, vec![SqlValue::Null]),
954 Row::new(2, vec![SqlValue::Null]),
955 Row::new(3, vec![SqlValue::Integer(1)]),
956 ];
957 let schema = vec![ColumnMetadata::new("id", ResolvedType::Integer)];
958 let input = VecIterator::new(rows, schema);
959 let mut limit = LimitIterator::with_ties(input, Some(1), None, vec![score_sort_key()]);
960
961 let mut results = Vec::new();
962 while let Some(Ok(row)) = limit.next_row() {
963 results.push(row.row_id);
964 }
965 assert_eq!(results, vec![1, 2]);
966 }
967
968 #[test]
969 fn limit_iterator_with_ties_zero_limit_yields_nothing() {
970 let rows = sample_rows();
971 let schema = sample_schema();
972 let input = VecIterator::new(rows, schema);
973 let mut limit = LimitIterator::with_ties(input, Some(0), None, vec![score_sort_key()]);
974 assert!(limit.next_row().is_none());
975 }
976
977 #[test]
978 fn sort_iterator_sorts_rows() {
979 use crate::planner::typed_expr::{SortExpr, TypedExpr, TypedExprKind};
980
981 let rows = vec![
982 Row::new(
983 1,
984 vec![SqlValue::Integer(3), SqlValue::Text("carol".into())],
985 ),
986 Row::new(
987 2,
988 vec![SqlValue::Integer(1), SqlValue::Text("alice".into())],
989 ),
990 Row::new(3, vec![SqlValue::Integer(2), SqlValue::Text("bob".into())]),
991 ];
992 let schema = sample_schema();
993 let input = VecIterator::new(rows, schema);
994
995 let order_by = vec![SortExpr {
997 expr: TypedExpr {
998 kind: TypedExprKind::ColumnRef {
999 table: "test".into(),
1000 column: "id".into(),
1001 column_index: 0,
1002 },
1003 resolved_type: ResolvedType::Integer,
1004 span: Span::default(),
1005 },
1006 asc: true,
1007 nulls_first: false,
1008 }];
1009
1010 let mut sort = SortIterator::new(input, &order_by).unwrap();
1011
1012 let mut results = Vec::new();
1013 while let Some(Ok(row)) = sort.next_row() {
1014 results.push(row);
1015 }
1016
1017 assert_eq!(results.len(), 3);
1018 assert_eq!(results[0].values[0], SqlValue::Integer(1));
1019 assert_eq!(results[1].values[0], SqlValue::Integer(2));
1020 assert_eq!(results[2].values[0], SqlValue::Integer(3));
1021 }
1022
1023 #[test]
1024 fn sort_iterator_memory_limit_exceeded_returns_resource_exhausted() {
1025 use crate::executor::memory::SpillPolicy;
1026
1027 let rows = sample_rows();
1028 let schema = sample_schema();
1029 let input = VecIterator::new(rows, schema);
1030 let policy = MemoryPolicy::new(Some(1), SpillPolicy::FailFast);
1031
1032 let err = match SortIterator::new_with_policy(input, &[], Some(policy)) {
1033 Ok(_) => panic!("expected sort iterator memory limit error"),
1034 Err(err) => err,
1035 };
1036
1037 assert!(matches!(err, ExecutorError::ResourceExhausted { .. }));
1038 }
1039
1040 #[test]
1041 fn sort_iterator_sorts_descending() {
1042 use crate::planner::typed_expr::{SortExpr, TypedExpr, TypedExprKind};
1043
1044 let rows = vec![
1045 Row::new(
1046 1,
1047 vec![SqlValue::Integer(1), SqlValue::Text("alice".into())],
1048 ),
1049 Row::new(
1050 2,
1051 vec![SqlValue::Integer(3), SqlValue::Text("carol".into())],
1052 ),
1053 Row::new(3, vec![SqlValue::Integer(2), SqlValue::Text("bob".into())]),
1054 ];
1055 let schema = sample_schema();
1056 let input = VecIterator::new(rows, schema);
1057
1058 let order_by = vec![SortExpr {
1060 expr: TypedExpr {
1061 kind: TypedExprKind::ColumnRef {
1062 table: "test".into(),
1063 column: "id".into(),
1064 column_index: 0,
1065 },
1066 resolved_type: ResolvedType::Integer,
1067 span: Span::default(),
1068 },
1069 asc: false,
1070 nulls_first: false,
1071 }];
1072
1073 let mut sort = SortIterator::new(input, &order_by).unwrap();
1074
1075 let mut results = Vec::new();
1076 while let Some(Ok(row)) = sort.next_row() {
1077 results.push(row);
1078 }
1079
1080 assert_eq!(results.len(), 3);
1081 assert_eq!(results[0].values[0], SqlValue::Integer(3));
1082 assert_eq!(results[1].values[0], SqlValue::Integer(2));
1083 assert_eq!(results[2].values[0], SqlValue::Integer(1));
1084 }
1085
1086 #[test]
1087 fn composed_pipeline_filter_then_limit() {
1088 use crate::ast::expr::BinaryOp;
1089 use crate::planner::typed_expr::{TypedExpr, TypedExprKind};
1090
1091 let rows = sample_rows();
1092 let schema = sample_schema();
1093 let input = VecIterator::new(rows, schema);
1094
1095 let predicate = TypedExpr {
1097 kind: TypedExprKind::BinaryOp {
1098 left: Box::new(TypedExpr {
1099 kind: TypedExprKind::ColumnRef {
1100 table: "test".into(),
1101 column: "id".into(),
1102 column_index: 0,
1103 },
1104 resolved_type: ResolvedType::Integer,
1105 span: Span::default(),
1106 }),
1107 op: BinaryOp::Gt,
1108 right: Box::new(TypedExpr::literal(
1109 crate::ast::expr::Literal::Number("1".into()),
1110 ResolvedType::Integer,
1111 Span::default(),
1112 )),
1113 },
1114 resolved_type: ResolvedType::Boolean,
1115 span: Span::default(),
1116 };
1117
1118 let filtered = FilterIterator::new(input, predicate);
1119 let mut limited = LimitIterator::new(filtered, Some(2), None);
1120
1121 let mut results = Vec::new();
1122 while let Some(Ok(row)) = limited.next_row() {
1123 results.push(row);
1124 }
1125
1126 assert_eq!(results.len(), 2);
1128 assert_eq!(results[0].row_id, 2);
1129 assert_eq!(results[1].row_id, 3);
1130 }
1131}