Skip to main content

drive/query/drive_document_sum_query/executors/
total.rs

1//! Total-sum executor for [`super::super::DocumentSumMode::Total`]
2//! dispatch — `prove = false` sum queries without a range clause.
3//!
4//! Mirror of count's `executors/total.rs` with the substitutions
5//! documented in `executors/mod.rs` (count → sum, u64 → i64,
6//! count_value_or_default → sum_value_or_default).
7
8use super::super::index_picker::find_summable_index_for_where_clauses;
9use super::super::{DriveDocumentSumQuery, SumEntry};
10use crate::drive::Drive;
11use crate::error::query::QuerySyntaxError;
12use crate::error::Error;
13use crate::query::ResolvedTimeRange;
14use crate::query::WhereClause;
15use dpp::data_contract::document_type::DocumentTypeRef;
16use dpp::version::PlatformVersion;
17use grovedb::TransactionArg;
18
19impl Drive {
20    /// Total sum for the given where clauses against an exactly-
21    /// covering summable index, OR — when the where clauses are
22    /// empty and the document type has `documents_summable: Some(_)`
23    /// — the type's primary-key SumTree (O(1) read at the doctype
24    /// tree's root).
25    ///
26    /// Single summed entry with empty key.
27    #[allow(clippy::too_many_arguments)]
28    pub fn execute_document_sum_total_no_proof(
29        &self,
30        contract_id: [u8; 32],
31        document_type: DocumentTypeRef,
32        document_type_name: String,
33        where_clauses: Vec<WhereClause>,
34        resolved_time_ranges: &[ResolvedTimeRange],
35        sum_property: String,
36        transaction: TransactionArg,
37        platform_version: &PlatformVersion,
38    ) -> Result<Vec<SumEntry>, Error> {
39        use dpp::data_contract::document_type::accessors::{
40            DocumentTypeV0Getters, DocumentTypeV2Getters,
41        };
42
43        // Fast path: unfiltered total sum on a `documents_summable:
44        // Some(matching_property)` doctype reads the primary-key
45        // SumTree directly (O(1)). No index needed — the doctype tree
46        // itself carries the sum.
47        if where_clauses.is_empty()
48            && document_type
49                .documents_summable()
50                .map(|p| p == sum_property)
51                .unwrap_or(false)
52        {
53            let sum = self.read_primary_key_sum_tree(
54                &contract_id,
55                &document_type_name,
56                transaction,
57                platform_version,
58            )?;
59            return Ok(vec![SumEntry {
60                in_key: None,
61                key: vec![],
62                sum: Some(sum),
63            }]);
64        }
65
66        let index = find_summable_index_for_where_clauses(
67            document_type.indexes(),
68            &where_clauses,
69            &sum_property,
70            resolved_time_ranges,
71        )
72        .ok_or_else(|| {
73            Error::Query(QuerySyntaxError::WhereClauseOnNonIndexedProperty(
74                "sum query requires a `summable: \"<prop>\"` index whose properties \
75                 exactly match the where clause fields and whose summed property \
76                 matches the request's `sum_property`, or `documentsSummable: \
77                 \"<prop>\"` on the document type for unfiltered total sums"
78                    .to_string(),
79            ))
80        })?;
81        let sum_query = DriveDocumentSumQuery {
82            document_type,
83            contract_id,
84            document_type_name,
85            index,
86            where_clauses,
87            sum_property,
88        };
89        sum_query.execute_no_proof(self, transaction, platform_version)
90    }
91
92    /// Reads the document-type primary-key tree's `SumTree` element
93    /// (`[contract_doc, contract_id, [1], doctype, 0]`) and returns
94    /// `sum_value_or_default()`. Used by the `documents_summable:
95    /// Some(_)` fast path on the total-sum flow.
96    ///
97    /// `insert_contract_operations_v0` unconditionally creates a
98    /// sum-bearing tree at `[..., doctype, 0]` for every applied
99    /// document type whose `documents_summable` is set, so a missing
100    /// element here indicates contract-state corruption or a
101    /// mis-applied contract — fail fast rather than silently
102    /// returning 0.
103    pub(super) fn read_primary_key_sum_tree(
104        &self,
105        contract_id: &[u8; 32],
106        document_type_name: &str,
107        transaction: TransactionArg,
108        platform_version: &PlatformVersion,
109    ) -> Result<i64, Error> {
110        let drive_version = &platform_version.drive;
111        let path = [
112            &[crate::drive::RootTree::DataContractDocuments as u8] as &[u8],
113            contract_id,
114            &[1u8],
115            document_type_name.as_bytes(),
116        ];
117        let mut drive_operations = vec![];
118        let element = self
119            .grove_get_raw_optional(
120                grovedb_path::SubtreePath::from(path.as_slice()),
121                &[0],
122                crate::util::grove_operations::DirectQueryType::StatefulDirectQuery,
123                transaction,
124                &mut drive_operations,
125                drive_version,
126            )?
127            .ok_or_else(|| {
128                Error::Drive(crate::error::drive::DriveError::CorruptedCodeExecution(
129                    "missing primary-key sum tree for an applied document type — \
130                     insert_contract_operations_v0 must have created it",
131                ))
132            })?;
133        Ok(element.sum_value_or_default())
134    }
135}