1use hawdb_core::{HawDBError, 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 let output_rows = max_rows.checked_add(1).ok_or_else(|| {
32 HawDBError::Execution("read query row limit is too large".to_string())
33 })?;
34 Ok(Self {
35 output_rows: Some(output_rows),
36 })
37 }
38
39 pub fn child_for_limit(self, offset: usize, limit: Option<usize>) -> Self {
40 let output_rows = match (self.output_rows, limit) {
41 (Some(cap), Some(limit)) => Some(offset.saturating_add(cap.min(limit))),
42 (Some(cap), None) => Some(offset.saturating_add(cap)),
43 (None, Some(limit)) => Some(offset.saturating_add(limit)),
44 (None, None) => None,
45 };
46 Self { output_rows }
47 }
48
49 pub fn is_reached(self, len: usize) -> bool {
50 self.output_rows.is_some_and(|cap| len >= cap)
51 }
52}
53
54#[cfg(test)]
55mod tests {
56 use super::*;
57
58 #[test]
59 fn user_limit_keeps_one_extra_detection_row() {
60 let limit = ExecutionLimit::from_user_max_rows(Some(10)).unwrap();
61 assert_eq!(limit.output_rows, Some(11));
62 assert!(limit.is_reached(11));
63 assert!(!limit.is_reached(10));
64 }
65
66 #[test]
67 fn nested_limit_keeps_parent_detection_cap() {
68 let limit = ExecutionLimit {
69 output_rows: Some(11),
70 };
71 assert_eq!(
72 limit.child_for_limit(5, Some(3)),
73 ExecutionLimit {
74 output_rows: Some(8)
75 }
76 );
77 }
78}