drive/query/drive_document_sum_query/
drive_dispatcher.rs1use crate::config::DriveConfig;
14use crate::drive::Drive;
15use crate::error::query::QuerySyntaxError;
16use crate::error::Error;
17use crate::query::drive_document_count_query::drive_dispatcher as count_dispatcher;
18use crate::query::drive_document_sum_query::{
19 DocumentSumMode, DocumentSumRequest, DocumentSumResponse, RangeSumOptions, RangeSumWalkMode,
20 SumMode,
21};
22use crate::query::{
23 key_array_element_clauses, validate_and_canonicalize_where_clauses,
24 validate_resolved_time_range_clause_shapes, OrderClause, WhereClause,
25};
26use dpp::data_contract::accessors::v0::DataContractV0Getters;
27use dpp::data_contract::document_type::accessors::DocumentTypeV0Getters;
28use dpp::platform_value::Value;
29use dpp::version::PlatformVersion;
30use grovedb::TransactionArg;
31
32fn effective_no_proof_distinct_limit(
33 requested_limit: Option<u32>,
34 drive_config: &DriveConfig,
35) -> Result<u16, Error> {
36 let effective_limit = requested_limit
37 .unwrap_or(drive_config.default_query_limit as u32)
38 .min(drive_config.max_query_limit as u32);
39
40 if effective_limit == 0 {
41 return Err(Error::Query(QuerySyntaxError::InvalidLimit(
42 "effective distinct SUM limit must be greater than zero".to_string(),
43 )));
44 }
45
46 Ok(effective_limit as u16)
49}
50
51#[cfg(feature = "server")]
52impl Drive {
53 pub fn execute_document_sum_request(
59 &self,
60 mut request: DocumentSumRequest,
61 transaction: TransactionArg,
62 platform_version: &PlatformVersion,
63 ) -> Result<DocumentSumResponse, Error> {
64 request.where_clauses = key_array_element_clauses(
71 std::mem::take(&mut request.where_clauses),
72 request.document_type,
73 matches!(request.mode, SumMode::GroupByIn),
74 platform_version,
75 )?;
76 request.where_clauses =
77 validate_and_canonicalize_where_clauses(request.where_clauses, platform_version)?;
78 validate_resolved_time_range_clause_shapes(
81 &request.where_clauses,
82 &request.resolved_time_ranges,
83 )?;
84 let resolved_mode = super::mode_detection::detect_sum_mode(&request, platform_version)?;
85
86 let contract_id = request.contract.id().to_buffer();
87 let document_type_name = request.document_type.name().to_string();
88 let where_clauses = request.where_clauses;
89 let resolved_time_ranges = request.resolved_time_ranges;
90 let sum_property = request.sum_property;
91 let order_by_ascending = request
94 .order_clauses
95 .first()
96 .map(|c| c.ascending)
97 .unwrap_or(true);
98
99 match resolved_mode {
100 DocumentSumMode::Total => {
101 let entries = self.execute_document_sum_total_no_proof(
102 contract_id,
103 request.document_type,
104 document_type_name,
105 where_clauses,
106 &resolved_time_ranges,
107 sum_property,
108 transaction,
109 platform_version,
110 )?;
111 let total = entries.first().and_then(|e| e.sum).unwrap_or(0);
112 Ok(DocumentSumResponse::Aggregate(total))
113 }
114 DocumentSumMode::PerInValue => {
115 let options = RangeSumOptions {
116 walk_mode: RangeSumWalkMode::Aggregate,
117 carrier_outer_limit: None,
118 left_to_right: order_by_ascending,
119 };
120 Ok(DocumentSumResponse::Entries(
121 self.execute_document_sum_per_in_value_no_proof(
122 contract_id,
123 request.document_type,
124 document_type_name,
125 where_clauses,
126 &resolved_time_ranges,
127 sum_property,
128 options,
129 transaction,
130 platform_version,
131 )?,
132 ))
133 }
134 DocumentSumMode::RangeNoProof => {
135 let return_distinct = matches!(
136 request.mode,
137 SumMode::GroupByRange | SumMode::GroupByCompound
138 );
139 let walk_mode = if return_distinct {
140 RangeSumWalkMode::Distinct(effective_no_proof_distinct_limit(
141 request.limit,
142 request.drive_config,
143 )?)
144 } else {
145 RangeSumWalkMode::Aggregate
146 };
147 let options = RangeSumOptions {
148 walk_mode,
149 carrier_outer_limit: None,
150 left_to_right: order_by_ascending,
151 };
152 let entries = self.execute_document_sum_range_no_proof(
153 contract_id,
154 request.document_type,
155 document_type_name,
156 where_clauses,
157 &resolved_time_ranges,
158 sum_property,
159 options,
160 transaction,
161 platform_version,
162 )?;
163 if matches!(request.mode, SumMode::Aggregate) {
164 let total = entries.first().and_then(|e| e.sum).unwrap_or(0);
165 Ok(DocumentSumResponse::Aggregate(total))
166 } else {
167 Ok(DocumentSumResponse::Entries(entries))
168 }
169 }
170 DocumentSumMode::RangeProof => Ok(DocumentSumResponse::Proof(
171 self.execute_document_sum_range_proof(
172 contract_id,
173 request.document_type,
174 document_type_name,
175 where_clauses,
176 &resolved_time_ranges,
177 sum_property,
178 transaction,
179 platform_version,
180 )?,
181 )),
182 DocumentSumMode::RangeDistinctProof => {
183 let effective_limit = request
204 .limit
205 .unwrap_or(crate::config::DEFAULT_QUERY_LIMIT as u32);
206 if effective_limit > request.drive_config.max_query_limit as u32 {
207 return Err(Error::Query(
208 crate::error::query::QuerySyntaxError::InvalidLimit(format!(
209 "limit {} exceeds max_query_limit {} on the prove + \
210 distinct-walk path (GROUP BY a range field, SUM); \
211 reduce the requested limit or use prove = false",
212 effective_limit, request.drive_config.max_query_limit
213 )),
214 ));
215 }
216 let limit_u16 = u16::try_from(effective_limit).map_err(|_| {
217 Error::Query(crate::error::query::QuerySyntaxError::Unsupported(format!(
218 "limit {} exceeds u16::MAX for range-distinct sum proof",
219 effective_limit
220 )))
221 })?;
222 Ok(DocumentSumResponse::Proof(
223 self.execute_document_sum_range_distinct_proof(
224 contract_id,
225 request.document_type,
226 document_type_name,
227 where_clauses,
228 &resolved_time_ranges,
229 sum_property,
230 limit_u16,
231 order_by_ascending,
232 transaction,
233 platform_version,
234 )?,
235 ))
236 }
237 DocumentSumMode::PointLookupProof => Ok(DocumentSumResponse::Proof(
238 self.execute_document_sum_point_lookup_proof(
239 contract_id,
240 request.document_type,
241 document_type_name,
242 where_clauses,
243 &resolved_time_ranges,
244 sum_property,
245 transaction,
246 platform_version,
247 )?,
248 )),
249 DocumentSumMode::RangeAggregateCarrierProof => {
250 let limit_u16 = request
260 .limit
261 .map(|l| {
262 if l > request.drive_config.max_query_limit as u32 {
263 return Err(Error::Query(
264 crate::error::query::QuerySyntaxError::InvalidLimit(format!(
265 "limit {} exceeds max_query_limit {} on the prove + \
266 carrier-aggregate path (GROUP BY In + range, SUM); \
267 reduce the requested limit or use prove = false",
268 l, request.drive_config.max_query_limit
269 )),
270 ));
271 }
272 u16::try_from(l).map_err(|_| {
273 Error::Query(crate::error::query::QuerySyntaxError::Unsupported(
274 format!(
275 "limit {} exceeds u16::MAX for carrier-aggregate sum proof",
276 l
277 ),
278 ))
279 })
280 })
281 .transpose()?;
282 Ok(DocumentSumResponse::Proof(
283 self.execute_document_sum_range_aggregate_carrier_proof(
284 contract_id,
285 request.document_type,
286 document_type_name,
287 where_clauses,
288 &resolved_time_ranges,
289 sum_property,
290 limit_u16,
291 order_by_ascending,
292 transaction,
293 platform_version,
294 )?,
295 ))
296 }
297 }
298 }
299}
300
301pub fn where_clauses_from_value(
309 value: &Value,
310 platform_version: &PlatformVersion,
311) -> Result<Vec<WhereClause>, Error> {
312 count_dispatcher::where_clauses_from_value(value, platform_version)
313}
314
315pub fn order_clauses_from_value(value: &Value) -> Result<Vec<OrderClause>, Error> {
318 crate::query::drive_document_count_query::drive_dispatcher::order_clauses_from_value(value)
319}
320
321#[cfg(test)]
322mod tests {
323 use super::effective_no_proof_distinct_limit;
324 use crate::config::DriveConfig;
325
326 #[test]
327 fn no_proof_distinct_limit_uses_the_default_and_clamps_to_the_maximum() {
328 let config = DriveConfig {
329 default_query_limit: 25,
330 max_query_limit: 100,
331 ..DriveConfig::default()
332 };
333
334 assert_eq!(
335 effective_no_proof_distinct_limit(None, &config).unwrap(),
336 25
337 );
338 assert_eq!(
339 effective_no_proof_distinct_limit(Some(7), &config).unwrap(),
340 7
341 );
342 assert_eq!(
343 effective_no_proof_distinct_limit(Some(10_000), &config).unwrap(),
344 100
345 );
346
347 let disabled = DriveConfig {
348 max_query_limit: 0,
349 ..config
350 };
351 assert!(effective_no_proof_distinct_limit(None, &disabled).is_err());
352 }
353}