1pub const MAX_APPLICATION_QUERY_STEPS: u64 = 64;
5pub const MAX_APPLICATION_QUERY_RESULT_BYTES: u64 = 4_194_304;
7pub const MAX_APPLICATION_QUERY_SCANNED_ROWS: u64 = 65_535;
12pub const MAX_APPLICATION_QUERY_PAGE_ROWS: u64 = MAX_APPLICATION_QUERY_SCANNED_ROWS - 1;
14pub const MAX_APPLICATION_QUERY_PAGE_ROWS_BOUNDED_LIMIT_V1: u64 = 499;
16pub const MAX_EXACT_VECTOR_PARTITION_ROWS_V1: u64 = 500;
18
19#[derive(Clone, Copy, Debug, Eq, PartialEq)]
25pub struct QueryCostVectorV1 {
26 access_steps: u64,
27 scanned_index_rows: u64,
28 point_reads: u64,
29 dependent_keys: u64,
30 intermediate_rows: u64,
31 projected_values: u64,
32 encoded_result_bytes: u64,
33}
34
35impl QueryCostVectorV1 {
36 #[allow(clippy::too_many_arguments)]
39 #[must_use]
40 pub const fn new(
41 access_steps: u64,
42 scanned_index_rows: u64,
43 point_reads: u64,
44 dependent_keys: u64,
45 intermediate_rows: u64,
46 projected_values: u64,
47 encoded_result_bytes: u64,
48 ) -> Option<Self> {
49 if access_steps == 0
50 || access_steps > MAX_APPLICATION_QUERY_STEPS
51 || encoded_result_bytes > MAX_APPLICATION_QUERY_RESULT_BYTES
52 {
53 return None;
54 }
55 Some(Self {
56 access_steps,
57 scanned_index_rows,
58 point_reads,
59 dependent_keys,
60 intermediate_rows,
61 projected_values,
62 encoded_result_bytes,
63 })
64 }
65
66 #[must_use]
68 pub const fn zero() -> Self {
69 Self {
70 access_steps: 0,
71 scanned_index_rows: 0,
72 point_reads: 0,
73 dependent_keys: 0,
74 intermediate_rows: 0,
75 projected_values: 0,
76 encoded_result_bytes: 0,
77 }
78 }
79
80 #[must_use]
82 pub const fn access_steps(self) -> u64 {
83 self.access_steps
84 }
85
86 #[must_use]
88 pub const fn scanned_index_rows(self) -> u64 {
89 self.scanned_index_rows
90 }
91
92 #[must_use]
94 pub const fn point_reads(self) -> u64 {
95 self.point_reads
96 }
97
98 #[must_use]
100 pub const fn dependent_keys(self) -> u64 {
101 self.dependent_keys
102 }
103
104 #[must_use]
106 pub const fn intermediate_rows(self) -> u64 {
107 self.intermediate_rows
108 }
109
110 #[must_use]
112 pub const fn projected_values(self) -> u64 {
113 self.projected_values
114 }
115
116 #[must_use]
118 pub const fn encoded_result_bytes(self) -> u64 {
119 self.encoded_result_bytes
120 }
121
122 #[must_use]
124 pub const fn covers(self, requested: Self) -> bool {
125 requested.access_steps <= self.access_steps
126 && requested.scanned_index_rows <= self.scanned_index_rows
127 && requested.point_reads <= self.point_reads
128 && requested.dependent_keys <= self.dependent_keys
129 && requested.intermediate_rows <= self.intermediate_rows
130 && requested.projected_values <= self.projected_values
131 && requested.encoded_result_bytes <= self.encoded_result_bytes
132 }
133}
134
135crate::inspect_owned_capacity_copy_v1!(QueryCostVectorV1);
137
138#[cfg(test)]
139mod tests {
140 use super::*;
141
142 #[test]
143 fn complete_vector_coverage_rejects_one_over_in_every_dimension() {
144 let budget = QueryCostVectorV1::new(7, 11, 13, 17, 19, 23, 29).expect("budget");
145 assert!(budget.covers(budget));
146 let candidates = [
147 QueryCostVectorV1::new(8, 11, 13, 17, 19, 23, 29),
148 QueryCostVectorV1::new(7, 12, 13, 17, 19, 23, 29),
149 QueryCostVectorV1::new(7, 11, 14, 17, 19, 23, 29),
150 QueryCostVectorV1::new(7, 11, 13, 18, 19, 23, 29),
151 QueryCostVectorV1::new(7, 11, 13, 17, 20, 23, 29),
152 QueryCostVectorV1::new(7, 11, 13, 17, 19, 24, 29),
153 QueryCostVectorV1::new(7, 11, 13, 17, 19, 23, 30),
154 ];
155 assert!(
156 candidates
157 .into_iter()
158 .all(|candidate| { candidate.is_none_or(|candidate| !budget.covers(candidate)) })
159 );
160 }
161
162 #[test]
163 fn empty_work_is_a_valid_accounting_value_but_not_a_compiled_plan() {
164 assert_eq!(QueryCostVectorV1::zero().access_steps(), 0);
165 assert!(QueryCostVectorV1::new(0, 0, 0, 0, 0, 0, 0).is_none());
166 assert!(
167 QueryCostVectorV1::new(1, 0, 0, 0, 0, 0, MAX_APPLICATION_QUERY_RESULT_BYTES).is_some()
168 );
169 assert!(
170 QueryCostVectorV1::new(1, 0, 0, 0, 0, 0, MAX_APPLICATION_QUERY_RESULT_BYTES + 1,)
171 .is_none()
172 );
173 }
174
175 #[test]
176 fn page_rows_reserve_exactly_one_continuation_probe() {
177 assert_eq!(MAX_APPLICATION_QUERY_SCANNED_ROWS, 65_535);
178 assert_eq!(MAX_APPLICATION_QUERY_PAGE_ROWS, 65_534);
179 assert_eq!(MAX_APPLICATION_QUERY_PAGE_ROWS_BOUNDED_LIMIT_V1, 499);
180 assert_eq!(MAX_EXACT_VECTOR_PARTITION_ROWS_V1, 500);
181 }
182}