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 validate_and_canonicalize_where_clauses, validate_resolved_time_range_clause_shapes,
24 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 =
69 validate_and_canonicalize_where_clauses(request.where_clauses, platform_version)?;
70 validate_resolved_time_range_clause_shapes(
73 &request.where_clauses,
74 &request.resolved_time_ranges,
75 )?;
76 let resolved_mode = super::mode_detection::detect_sum_mode(&request, platform_version)?;
77
78 let contract_id = request.contract.id().to_buffer();
79 let document_type_name = request.document_type.name().to_string();
80 let where_clauses = request.where_clauses;
81 let resolved_time_ranges = request.resolved_time_ranges;
82 let sum_property = request.sum_property;
83 let order_by_ascending = request
86 .order_clauses
87 .first()
88 .map(|c| c.ascending)
89 .unwrap_or(true);
90
91 match resolved_mode {
92 DocumentSumMode::Total => {
93 let entries = self.execute_document_sum_total_no_proof(
94 contract_id,
95 request.document_type,
96 document_type_name,
97 where_clauses,
98 &resolved_time_ranges,
99 sum_property,
100 transaction,
101 platform_version,
102 )?;
103 let total = entries.first().and_then(|e| e.sum).unwrap_or(0);
104 Ok(DocumentSumResponse::Aggregate(total))
105 }
106 DocumentSumMode::PerInValue => {
107 let options = RangeSumOptions {
108 walk_mode: RangeSumWalkMode::Aggregate,
109 carrier_outer_limit: None,
110 left_to_right: order_by_ascending,
111 };
112 Ok(DocumentSumResponse::Entries(
113 self.execute_document_sum_per_in_value_no_proof(
114 contract_id,
115 request.document_type,
116 document_type_name,
117 where_clauses,
118 &resolved_time_ranges,
119 sum_property,
120 options,
121 transaction,
122 platform_version,
123 )?,
124 ))
125 }
126 DocumentSumMode::RangeNoProof => {
127 let return_distinct = matches!(
128 request.mode,
129 SumMode::GroupByRange | SumMode::GroupByCompound
130 );
131 let walk_mode = if return_distinct {
132 RangeSumWalkMode::Distinct(effective_no_proof_distinct_limit(
133 request.limit,
134 request.drive_config,
135 )?)
136 } else {
137 RangeSumWalkMode::Aggregate
138 };
139 let options = RangeSumOptions {
140 walk_mode,
141 carrier_outer_limit: None,
142 left_to_right: order_by_ascending,
143 };
144 let entries = self.execute_document_sum_range_no_proof(
145 contract_id,
146 request.document_type,
147 document_type_name,
148 where_clauses,
149 &resolved_time_ranges,
150 sum_property,
151 options,
152 transaction,
153 platform_version,
154 )?;
155 if matches!(request.mode, SumMode::Aggregate) {
156 let total = entries.first().and_then(|e| e.sum).unwrap_or(0);
157 Ok(DocumentSumResponse::Aggregate(total))
158 } else {
159 Ok(DocumentSumResponse::Entries(entries))
160 }
161 }
162 DocumentSumMode::RangeProof => Ok(DocumentSumResponse::Proof(
163 self.execute_document_sum_range_proof(
164 contract_id,
165 request.document_type,
166 document_type_name,
167 where_clauses,
168 &resolved_time_ranges,
169 sum_property,
170 transaction,
171 platform_version,
172 )?,
173 )),
174 DocumentSumMode::RangeDistinctProof => {
175 let effective_limit = request
196 .limit
197 .unwrap_or(crate::config::DEFAULT_QUERY_LIMIT as u32);
198 if effective_limit > request.drive_config.max_query_limit as u32 {
199 return Err(Error::Query(
200 crate::error::query::QuerySyntaxError::InvalidLimit(format!(
201 "limit {} exceeds max_query_limit {} on the prove + \
202 distinct-walk path (GROUP BY a range field, SUM); \
203 reduce the requested limit or use prove = false",
204 effective_limit, request.drive_config.max_query_limit
205 )),
206 ));
207 }
208 let limit_u16 = u16::try_from(effective_limit).map_err(|_| {
209 Error::Query(crate::error::query::QuerySyntaxError::Unsupported(format!(
210 "limit {} exceeds u16::MAX for range-distinct sum proof",
211 effective_limit
212 )))
213 })?;
214 Ok(DocumentSumResponse::Proof(
215 self.execute_document_sum_range_distinct_proof(
216 contract_id,
217 request.document_type,
218 document_type_name,
219 where_clauses,
220 &resolved_time_ranges,
221 sum_property,
222 limit_u16,
223 order_by_ascending,
224 transaction,
225 platform_version,
226 )?,
227 ))
228 }
229 DocumentSumMode::PointLookupProof => Ok(DocumentSumResponse::Proof(
230 self.execute_document_sum_point_lookup_proof(
231 contract_id,
232 request.document_type,
233 document_type_name,
234 where_clauses,
235 &resolved_time_ranges,
236 sum_property,
237 transaction,
238 platform_version,
239 )?,
240 )),
241 DocumentSumMode::RangeAggregateCarrierProof => {
242 let limit_u16 = request
252 .limit
253 .map(|l| {
254 if l > request.drive_config.max_query_limit as u32 {
255 return Err(Error::Query(
256 crate::error::query::QuerySyntaxError::InvalidLimit(format!(
257 "limit {} exceeds max_query_limit {} on the prove + \
258 carrier-aggregate path (GROUP BY In + range, SUM); \
259 reduce the requested limit or use prove = false",
260 l, request.drive_config.max_query_limit
261 )),
262 ));
263 }
264 u16::try_from(l).map_err(|_| {
265 Error::Query(crate::error::query::QuerySyntaxError::Unsupported(
266 format!(
267 "limit {} exceeds u16::MAX for carrier-aggregate sum proof",
268 l
269 ),
270 ))
271 })
272 })
273 .transpose()?;
274 Ok(DocumentSumResponse::Proof(
275 self.execute_document_sum_range_aggregate_carrier_proof(
276 contract_id,
277 request.document_type,
278 document_type_name,
279 where_clauses,
280 &resolved_time_ranges,
281 sum_property,
282 limit_u16,
283 order_by_ascending,
284 transaction,
285 platform_version,
286 )?,
287 ))
288 }
289 }
290 }
291}
292
293pub fn where_clauses_from_value(
301 value: &Value,
302 platform_version: &PlatformVersion,
303) -> Result<Vec<WhereClause>, Error> {
304 count_dispatcher::where_clauses_from_value(value, platform_version)
305}
306
307pub fn order_clauses_from_value(value: &Value) -> Result<Vec<OrderClause>, Error> {
310 crate::query::drive_document_count_query::drive_dispatcher::order_clauses_from_value(value)
311}
312
313#[cfg(test)]
314mod tests {
315 use super::effective_no_proof_distinct_limit;
316 use crate::config::DriveConfig;
317
318 #[test]
319 fn no_proof_distinct_limit_uses_the_default_and_clamps_to_the_maximum() {
320 let config = DriveConfig {
321 default_query_limit: 25,
322 max_query_limit: 100,
323 ..DriveConfig::default()
324 };
325
326 assert_eq!(
327 effective_no_proof_distinct_limit(None, &config).unwrap(),
328 25
329 );
330 assert_eq!(
331 effective_no_proof_distinct_limit(Some(7), &config).unwrap(),
332 7
333 );
334 assert_eq!(
335 effective_no_proof_distinct_limit(Some(10_000), &config).unwrap(),
336 100
337 );
338
339 let disabled = DriveConfig {
340 max_query_limit: 0,
341 ..config
342 };
343 assert!(effective_no_proof_distinct_limit(None, &disabled).is_err());
344 }
345}