1use hawdb_core::Result;
16
17#[derive(Debug, Clone, Copy, PartialEq, Eq)]
18pub struct ExecutionLimit {
19 pub output_rows: Option<usize>,
20}
21
22impl ExecutionLimit {
23 pub const fn unlimited() -> Self {
24 Self { output_rows: None }
25 }
26
27 pub fn from_user_max_rows(max_rows: Option<usize>) -> Result<Self> {
28 let Some(max_rows) = max_rows else {
29 return Ok(Self::unlimited());
30 };
31 Ok(Self {
34 output_rows: max_rows.checked_add(1),
35 })
36 }
37
38 pub fn child_for_limit(self, offset: usize, limit: Option<usize>) -> Self {
39 let output_rows = match (self.output_rows, limit) {
40 (Some(cap), Some(limit)) => Some(offset.saturating_add(cap.min(limit))),
41 (Some(cap), None) => Some(offset.saturating_add(cap)),
42 (None, Some(limit)) => Some(offset.saturating_add(limit)),
43 (None, None) => None,
44 };
45 Self { output_rows }
46 }
47
48 pub fn is_reached(self, len: usize) -> bool {
49 self.output_rows.is_some_and(|cap| len >= cap)
50 }
51}
52
53#[cfg(test)]
54mod tests {
55 use super::*;
56
57 #[test]
58 fn user_limit_keeps_one_extra_detection_row() {
59 let limit = ExecutionLimit::from_user_max_rows(Some(10)).unwrap();
60 assert_eq!(limit.output_rows, Some(11));
61 assert!(limit.is_reached(11));
62 assert!(!limit.is_reached(10));
63 }
64
65 #[test]
66 fn nested_limit_keeps_parent_detection_cap() {
67 let limit = ExecutionLimit {
68 output_rows: Some(11),
69 };
70 assert_eq!(
71 limit.child_for_limit(5, Some(3)),
72 ExecutionLimit {
73 output_rows: Some(8)
74 }
75 );
76 }
77}