1use crate::error::drive::DriveError;
84use crate::error::proof::ProofError;
85use crate::error::query::QuerySyntaxError;
86use crate::error::Error;
87use crate::query::drive_document_count_query::point_lookup_count_entries;
88use crate::query::index_only_synthesis::synthesize_index_only_document;
89use crate::query::{
90 DriveDocumentCountQuery, DriveDocumentQuery, InternalClauses, OrderClause, SplitCountEntry,
91 WhereClause, WhereOperator,
92};
93use dpp::data_contract::accessors::v0::DataContractV0Getters;
94use dpp::data_contract::document_type::accessors::{DocumentTypeV0Getters, DocumentTypeV2Getters};
95use dpp::data_contract::document_type::{
96 DocumentPropertyReferenceTarget, DocumentPropertyType, DocumentTypeRef,
97};
98use dpp::data_contract::DataContract;
99use dpp::document::serialization_traits::DocumentPlatformConversionMethodsV0;
100use dpp::document::{Document, DocumentV0Getters};
101use dpp::identifier::Identifier;
102use dpp::platform_value::btreemap_extensions::BTreeValueMapPathHelper;
103use dpp::platform_value::Value;
104use dpp::version::PlatformVersion;
105use grovedb::{Element, PathQuery};
106use std::collections::{BTreeMap, BTreeSet};
107
108pub const MAX_SUB_QUERIES: usize = 10;
113
114pub const MAX_BOUND_VALUES: usize = 100;
118
119#[derive(Debug, Clone, Copy, PartialEq, Eq)]
121pub enum BindingSource {
122 Page,
124 SubQuery(usize),
127}
128
129#[derive(Debug, Clone, PartialEq, Eq)]
132pub struct SubQueryBinding {
133 pub source: BindingSource,
135 pub source_property: String,
139 pub field: String,
142}
143
144#[derive(Debug, Clone, Copy, PartialEq, Eq)]
146pub enum SubQueryKind {
147 Documents,
149 Count,
152}
153
154#[derive(Debug, Clone, PartialEq)]
156pub struct DriveSubQuery<'a> {
157 pub contract: &'a DataContract,
159 pub document_type: DocumentTypeRef<'a>,
161 pub kind: SubQueryKind,
163 pub where_clauses: Vec<WhereClause>,
166 pub order_by: Vec<OrderClause>,
173 pub limit: Option<u16>,
179 pub binding: Option<SubQueryBinding>,
181}
182
183#[derive(Debug, Clone, PartialEq)]
185pub enum SubQueryResult {
186 Documents(Vec<Document>),
189 Counts(Vec<SplitCountEntry>),
192}
193
194impl SubQueryResult {
195 pub fn documents(&self) -> &[Document] {
197 match self {
198 Self::Documents(documents) => documents,
199 Self::Counts(_) => &[],
200 }
201 }
202
203 pub fn counts(&self) -> &[SplitCountEntry] {
205 match self {
206 Self::Counts(entries) => entries,
207 Self::Documents(_) => &[],
208 }
209 }
210}
211
212#[derive(Debug, Default)]
214pub struct CompositeDocumentsResult {
215 pub page_documents: Vec<Document>,
217 pub sub_results: Vec<SubQueryResult>,
219}
220
221type DerivedValues = Vec<Identifier>;
223
224pub(crate) type ProvedTrio = (Vec<Vec<u8>>, Vec<u8>, Option<Element>);
227
228pub(crate) type PresentTrio = (Vec<Vec<u8>>, Vec<u8>, Element);
230
231#[derive(Debug, Clone, Copy, PartialEq, Eq)]
233enum Component {
234 Page,
235 Sub(usize),
236}
237
238fn unsupported(message: String) -> Error {
239 Error::Query(QuerySyntaxError::Unsupported(message))
240}
241
242fn corrupted_proof(message: String) -> Error {
243 Error::Proof(ProofError::CorruptedProof(message))
244}
245
246fn merge_error_to_shape_error(error: grovedb::Error) -> Error {
250 match error {
251 grovedb::Error::NotSupported(message) => unsupported(format!(
252 "the composite query's components cannot be merged into one proof: {}",
253 message
254 )),
255 other => Error::from(other),
256 }
257}
258
259fn document_bound_value(document: &Document, field: &str) -> Result<Option<Identifier>, Error> {
262 use dpp::document::property_names::{ID, OWNER_ID};
263 if field == ID {
264 return Ok(Some(document.id()));
265 }
266 if field == OWNER_ID {
267 return Ok(Some(document.owner_id()));
268 }
269 let Some(value) = document
270 .properties()
271 .get_optional_at_path(field)
272 .ok()
273 .flatten()
274 else {
275 return Ok(None);
276 };
277 value.to_identifier().map(Some).map_err(|_| {
278 Error::Drive(DriveError::CorruptedCodeExecution(
279 "a bound composite property must decode as an identifier: validate() only \
280 admits identifier-typed properties",
281 ))
282 })
283}
284
285fn sorted_values(values: &[Identifier]) -> Vec<Identifier> {
289 let mut sorted = values.to_vec();
290 sorted.sort();
291 sorted
292}
293
294impl<'a> DriveSubQuery<'a> {
295 fn bound_field(&self) -> Option<&str> {
296 self.binding.as_ref().map(|binding| binding.field.as_str())
297 }
298
299 fn is_by_id_join(&self) -> bool {
300 self.bound_field() == Some(dpp::document::property_names::ID)
301 }
302}
303
304impl<'a> DriveDocumentQuery<'a> {
305 pub fn validate_composite(&self, platform_version: &PlatformVersion) -> Result<(), Error> {
314 if self.sub_queries.is_empty() {
315 return Err(unsupported(
316 "a composite query needs at least one sub-query; a page alone is a plain \
317 documents query"
318 .to_string(),
319 ));
320 }
321 if self.sub_queries.len() > MAX_SUB_QUERIES {
322 return Err(unsupported(format!(
323 "a composite query carries at most {} sub-queries, got {}",
324 MAX_SUB_QUERIES,
325 self.sub_queries.len(),
326 )));
327 }
328 let page_limit = match self.limit {
329 None => {
330 return Err(unsupported(
331 "composite queries require an explicit limit on the page: the page size \
332 bounds every derived sub-query"
333 .to_string(),
334 ));
335 }
336 Some(0) => {
337 return Err(unsupported(
338 "a composite page limit must be at least 1".to_string(),
339 ));
340 }
341 Some(limit) if limit as usize > MAX_BOUND_VALUES => {
342 return Err(unsupported(format!(
343 "a composite page limit of {} exceeds {}: a derived `IN` clause admits at \
344 most that many values",
345 limit, MAX_BOUND_VALUES,
346 )));
347 }
348 Some(limit) => limit,
349 };
350 if self.offset.is_some() {
351 return Err(unsupported(
352 "composite queries do not support a page offset; paginate with a range clause"
353 .to_string(),
354 ));
355 }
356 if self.start_at.is_some() {
357 return Err(unsupported(
358 "composite queries do not support a page cursor (startAt/startAfter); \
359 paginate with a range clause on the page's ordering property"
360 .to_string(),
361 ));
362 }
363 if self.page_is_by_ids() {
366 let ids = self.page_ids()?.len();
367 if (page_limit as usize) < ids {
368 return Err(unsupported(format!(
369 "a by-ids composite page addresses {} ids but its limit is {}: the ids \
370 bound the page, so the limit must cover them",
371 ids, page_limit,
372 )));
373 }
374 }
375 let direction = self.page_direction(platform_version)?;
378
379 for (index, sub_query) in self.sub_queries.iter().enumerate() {
380 self.validate_sub_query(index, sub_query, direction, platform_version)?;
381 }
382 self.validate_component_paths(platform_version)
383 }
384
385 fn validate_sub_query(
386 &self,
387 index: usize,
388 sub_query: &DriveSubQuery<'a>,
389 direction: bool,
390 platform_version: &PlatformVersion,
391 ) -> Result<(), Error> {
392 let label = |message: &str| unsupported(format!("sub-query {}: {}", index, message));
393
394 let Some(binding) = &sub_query.binding else {
395 if sub_query.kind == SubQueryKind::Count {
397 return Err(label(
398 "a count sub-query must be bound (`COUNT ... WHERE <field> IN <derived \
399 values> GROUP BY <field>`); unbound counts stay on the regular count \
400 surface",
401 ));
402 }
403 match sub_query.limit {
404 None => {
405 return Err(label(
406 "a sibling documents sub-query requires an explicit limit",
407 ));
408 }
409 Some(0) => {
410 return Err(label("a sibling's limit must be at least 1"));
411 }
412 Some(limit) if limit as usize > MAX_BOUND_VALUES => {
413 return Err(label(&format!(
414 "limit {} exceeds {}",
415 limit, MAX_BOUND_VALUES
416 )));
417 }
418 Some(_) => {}
419 }
420 self.sub_query_document_query_with_direction(
422 sub_query,
423 &[],
424 direction,
425 platform_version,
426 )?
427 .construct_path_query(None, platform_version)?;
428 return Ok(());
429 };
430
431 let (source_contract, source_type, source_is_index_only_query) = match binding.source {
433 BindingSource::Page => (
434 self.contract,
435 self.document_type,
436 self.document_type.index_only(),
437 ),
438 BindingSource::SubQuery(source_index) => {
439 if source_index >= index {
440 return Err(label("a binding may only reference an earlier sub-query"));
441 }
442 let source = &self.sub_queries[source_index];
443 if source.kind != SubQueryKind::Documents {
444 return Err(label("a binding must reference a documents sub-query"));
445 }
446 (
447 source.contract,
448 source.document_type,
449 source.document_type.index_only(),
450 )
451 }
452 };
453
454 let source_property_type: Option<&DocumentPropertyType> = {
457 use dpp::document::property_names::{ID, OWNER_ID};
458 if binding.source_property == ID || binding.source_property == OWNER_ID {
459 None
460 } else {
461 let Some(property) = source_type
462 .flattened_properties()
463 .get(binding.source_property.as_str())
464 else {
465 return Err(label(&format!(
466 "source property \"{}\" does not name a property of \"{}\"",
467 binding.source_property,
468 source_type.name(),
469 )));
470 };
471 if !matches!(
472 property.property_type,
473 DocumentPropertyType::Identifier
474 | DocumentPropertyType::IdentifierWithReference(_)
475 ) {
476 return Err(label(&format!(
477 "source property \"{}\" is not identifier-typed; composite bindings \
478 derive identifiers only",
479 binding.source_property,
480 )));
481 }
482 Some(&property.property_type)
483 }
484 };
485
486 if source_is_index_only_query {
489 let carries = |index: &dpp::data_contract::document_type::Index| {
490 index.terminal.as_deref() == Some(binding.source_property.as_str())
491 || index
492 .properties
493 .iter()
494 .any(|property| property.name == binding.source_property)
495 };
496 let (carried, index_name) = match binding.source {
497 BindingSource::Page => {
498 let index = self.index_only_query_index(platform_version)?;
499 (carries(index), index.name.clone())
500 }
501 BindingSource::SubQuery(source_index) => {
502 let source = &self.sub_queries[source_index];
503 let shape = self.sub_query_document_query_with_direction(
504 source,
505 &[Identifier::default()],
506 direction,
507 platform_version,
508 )?;
509 let index = shape.index_only_query_index(platform_version)?;
510 (carries(index), index.name.clone())
511 }
512 };
513 if !carried {
514 return Err(label(&format!(
515 "the indexOnly source resolves to index \"{}\", which does not carry the \
516 source property \"{}\"",
517 index_name, binding.source_property,
518 )));
519 }
520 }
521
522 if sub_query
523 .where_clauses
524 .iter()
525 .any(|clause| clause.field == binding.field)
526 {
527 return Err(label(&format!(
528 "the fixed clauses may not name the bound field \"{}\"; its `IN` clause is \
529 derived",
530 binding.field,
531 )));
532 }
533
534 if !sub_query.is_by_id_join() && binding.field != dpp::document::property_names::OWNER_ID {
540 let Some(property) = sub_query
541 .document_type
542 .flattened_properties()
543 .get(binding.field.as_str())
544 else {
545 return Err(label(&format!(
546 "bound field \"{}\" does not name a property of \"{}\"",
547 binding.field,
548 sub_query.document_type.name(),
549 )));
550 };
551 if !matches!(
552 property.property_type,
553 DocumentPropertyType::Identifier | DocumentPropertyType::IdentifierWithReference(_)
554 ) {
555 return Err(label(&format!(
556 "bound field \"{}\" is not identifier-typed; composite bindings derive \
557 identifiers only",
558 binding.field,
559 )));
560 }
561 }
562
563 match sub_query.kind {
564 SubQueryKind::Documents if sub_query.is_by_id_join() => {
565 if sub_query.document_type.index_only() {
566 return Err(label(
567 "a by-id join cannot target an indexOnly type: there is no \
568 primary-key tree to fetch from",
569 ));
570 }
571 if sub_query.limit.is_some() {
572 return Err(label(
573 "a by-id join takes no limit: every derived id must resolve, so \
574 completeness is set equality, not a page",
575 ));
576 }
577 if !sub_query.order_by.is_empty() {
578 return Err(label(
579 "a by-id join takes no ordering: results follow the derived ids' \
580 first appearance",
581 ));
582 }
583 match source_property_type {
587 Some(DocumentPropertyType::IdentifierWithReference(
588 DocumentPropertyReferenceTarget::PermanentDocument {
589 contract_id,
590 document_type_name,
591 ..
592 },
593 )) => {
594 let referenced_contract =
595 contract_id.unwrap_or_else(|| source_contract.id());
596 if referenced_contract != sub_query.contract.id()
597 || document_type_name != sub_query.document_type.name()
598 {
599 return Err(label(&format!(
600 "the source property's refersTo targets \"{}\", not this \
601 sub-query's type \"{}\"",
602 document_type_name,
603 sub_query.document_type.name(),
604 )));
605 }
606 }
607 _ => {
608 return Err(label(&format!(
609 "a by-id join needs a source property declaring `refersTo: \
610 permanentDocument` (\"{}\" does not): only a permanent-document \
611 reference guarantees every derived id resolves",
612 binding.source_property,
613 )));
614 }
615 }
616 }
617 SubQueryKind::Documents => {
618 let shape = self.sub_query_document_query_with_direction(
620 sub_query,
621 &[Identifier::default()],
622 direction,
623 platform_version,
624 )?;
625 shape.construct_path_query(None, platform_version)?;
626 if sub_query.document_type.index_only() {
627 let index = shape.index_only_query_index(platform_version)?;
630 let carried = index.terminal.as_deref() == Some(binding.field.as_str())
631 || index
632 .properties
633 .iter()
634 .any(|property| property.name == binding.field);
635 if !carried {
636 return Err(label(&format!(
637 "the indexOnly lookup resolves to index \"{}\", which does not \
638 carry the bound field \"{}\"",
639 index.name, binding.field,
640 )));
641 }
642 }
643 let value_bounded =
650 self.lookup_is_value_bounded(sub_query, binding, &shape, platform_version)?;
651 match (value_bounded, sub_query.limit) {
652 (true, Some(_)) => {
653 return Err(label(
654 "a value-bounded lookup (a unique index, or an indexOnly terminal \
655 with every prefix fixed, yields at most one row per derived \
656 value) takes no limit",
657 ));
658 }
659 (false, Some(0)) => {
660 return Err(label("a lookup's limit must be at least 1"));
661 }
662 (false, None) => {
663 return Err(label(
664 "a documents lookup on a non-unique index requires an explicit \
665 limit: it bounds the walk under each derived value",
666 ));
667 }
668 (false, Some(limit)) if limit as usize > MAX_BOUND_VALUES => {
669 return Err(label(&format!(
670 "limit {} exceeds {}",
671 limit, MAX_BOUND_VALUES
672 )));
673 }
674 _ => {}
675 }
676 }
677 SubQueryKind::Count => {
678 if sub_query.limit.is_some() {
679 return Err(label("a count sub-query takes no limit"));
680 }
681 if !sub_query.order_by.is_empty() {
682 return Err(label("a count sub-query takes no ordering"));
683 }
684 if sub_query.is_by_id_join() {
685 return Err(label(
686 "a count sub-query counts by an indexed property, not by `$id`",
687 ));
688 }
689 self.sub_query_count_query(sub_query, &[Identifier::default()], platform_version)?
691 .point_lookup_count_path_query(platform_version)?;
692 }
693 }
694 Ok(())
695 }
696
697 fn lookup_is_value_bounded(
704 &self,
705 sub_query: &DriveSubQuery<'a>,
706 binding: &SubQueryBinding,
707 shape: &DriveDocumentQuery<'a>,
708 platform_version: &PlatformVersion,
709 ) -> Result<bool, Error> {
710 let fixed_equalities: BTreeSet<&str> = sub_query
711 .where_clauses
712 .iter()
713 .filter(|clause| clause.operator == WhereOperator::Equal)
714 .map(|clause| clause.field.as_str())
715 .collect();
716 if sub_query.document_type.index_only() {
717 let index = shape.index_only_query_index(platform_version)?;
718 let terminal_is_bound = index.terminal.as_deref() == Some(binding.field.as_str());
719 let prefix_fixed = index
720 .properties
721 .iter()
722 .all(|property| fixed_equalities.contains(property.name.as_str()));
723 return Ok(terminal_is_bound && prefix_fixed);
724 }
725 let mut wanted: BTreeSet<&str> = fixed_equalities.clone();
726 wanted.insert(binding.field.as_str());
727 Ok(sub_query.document_type.indexes().values().any(|index| {
728 index.unique
729 && index.properties.len() == wanted.len()
730 && index
731 .properties
732 .iter()
733 .all(|property| wanted.contains(property.name.as_str()))
734 }))
735 }
736
737 fn page_is_by_ids(&self) -> bool {
739 self.internal_clauses.primary_key_in_clause.is_some()
740 || self.internal_clauses.primary_key_equal_clause.is_some()
741 }
742
743 fn budget_as_instance_cap(mut path_query: PathQuery) -> PathQuery {
756 if let Some(limit) = path_query.query.limit.take() {
757 path_query.query.query.limit = Some(limit);
758 }
759 path_query
760 }
761
762 pub fn page_path_query(&self, platform_version: &PlatformVersion) -> Result<PathQuery, Error> {
769 if self.page_is_by_ids() {
770 let mut unlimited = self.clone();
771 unlimited.limit = None;
772 let mut path_query = unlimited.construct_path_query(None, platform_version)?;
773 path_query.query.limit = None;
777 return Ok(path_query);
778 }
779 Ok(Self::budget_as_instance_cap(
780 self.construct_path_query(None, platform_version)?,
781 ))
782 }
783
784 fn validate_component_paths(&self, platform_version: &PlatformVersion) -> Result<(), Error> {
803 let representative = [Identifier::default()];
804 let mut components: Vec<(Vec<Vec<u8>>, Component, bool)> = Vec::new();
805 let page = self.page_path_query(platform_version)?;
806 let direction = page.query.query.left_to_right;
807 components.push((page.path, Component::Page, page.query.query.limit.is_some()));
808 for (index, sub_query) in self.sub_queries.iter().enumerate() {
809 let path_query = self.sub_query_proof_path_query(
810 sub_query,
811 &representative,
812 direction,
813 platform_version,
814 )?;
815 components.push((
816 path_query.path,
817 Component::Sub(index),
818 path_query.query.query.limit.is_some(),
819 ));
820 }
821
822 let is_bound = |component: &Component| matches!(component, Component::Sub(index) if self.sub_queries[*index].binding.is_some());
823 for (path, component, limited) in &components {
824 if !*limited {
825 continue;
826 }
827 let lands_at_root = match component {
828 Component::Page => {
832 let (siblings, bound): (Vec<_>, Vec<_>) = components
833 .iter()
834 .skip(1)
835 .partition(|(_, other, _)| !is_bound(other));
836 bound.iter().any(|(other, _, _)| other.starts_with(path))
837 || (!siblings.is_empty()
838 && siblings.iter().all(|(other, _, _)| other.starts_with(path)))
839 }
840 Component::Sub(_) => components
844 .iter()
845 .filter(|(_, other, _)| other != component && !is_bound(other))
846 .all(|(other, _, _)| other.starts_with(path)),
847 };
848 if lands_at_root {
849 return Err(unsupported(format!(
850 "{} carries a limit and lands at the merged root of the composite proof \
851 (once the bound sub-queries that derive nothing drop out), where grovedb \
852 refuses a budget; give it a clause that narrows its path, or split it \
853 into a separate request",
854 match component {
855 Component::Page => "the page".to_string(),
856 Component::Sub(index) => format!("sub-query {}", index),
857 }
858 )));
859 }
860 }
861
862 let mut groups: BTreeMap<&Vec<Vec<u8>>, Vec<(Component, bool)>> = BTreeMap::new();
863 for (path, component, limited) in &components {
864 groups.entry(path).or_default().push((*component, *limited));
865 }
866 for members in groups.values() {
867 let documents_members: Vec<Component> = members
868 .iter()
869 .map(|(component, _)| *component)
870 .filter(|component| match component {
871 Component::Page => true,
872 Component::Sub(index) => {
873 self.sub_queries[*index].kind == SubQueryKind::Documents
874 }
875 })
876 .collect();
877 let has_count_member = members.iter().any(|(component, _)| {
878 matches!(component, Component::Sub(index) if self.sub_queries[*index].kind == SubQueryKind::Count)
879 });
880 if has_count_member && !documents_members.is_empty() {
890 return Err(unsupported(
891 "a count sub-query shares its index path with a documents component: \
892 the count reads the index's value trees themselves while the documents \
893 query descends past them, and one proof cannot serve both; count on \
894 another index, or split them into separate requests"
895 .to_string(),
896 ));
897 }
898 if documents_members.len() < 2 {
899 continue;
900 }
901 let has_sibling = documents_members.iter().any(|component| {
902 matches!(component, Component::Sub(index) if self.sub_queries[*index].binding.is_none())
903 });
904 let has_page = documents_members.contains(&Component::Page);
905 let all_subs_are_joins = documents_members.iter().all(|component| match component {
906 Component::Page => true,
907 Component::Sub(index) => self.sub_queries[*index].is_by_id_join(),
908 });
909 if has_sibling || (has_page && !(self.page_is_by_ids() && all_subs_are_joins)) {
910 return Err(unsupported(
911 "two documents components of the composite query address the same index \
912 path and cannot be told apart by their derived values (a sibling, or a \
913 page that is not a by-ids fetch, shares a path with another component); \
914 split them into separate requests"
915 .to_string(),
916 ));
917 }
918 if members.iter().any(|(_, limited)| *limited) {
922 return Err(unsupported(
923 "two documents components of the composite query address the same index \
924 path and one of them carries a limit, which cannot be merged with the \
925 other's selection; split them into separate requests"
926 .to_string(),
927 ));
928 }
929 }
930 Ok(())
931 }
932
933 pub fn derive_values(
938 &self,
939 binding: &SubQueryBinding,
940 source_documents: &[Document],
941 ) -> Result<DerivedValues, Error> {
942 let mut seen: BTreeSet<Identifier> = BTreeSet::new();
943 let mut values = Vec::new();
944 for document in source_documents {
945 if let Some(value) = document_bound_value(document, &binding.source_property)? {
946 if seen.insert(value) {
947 values.push(value);
948 }
949 }
950 }
951 if values.len() > MAX_BOUND_VALUES {
952 return Err(Error::Drive(DriveError::CorruptedCodeExecution(
956 "a composite binding source yielded more documents than the shapes allow",
957 )));
958 }
959 Ok(values)
960 }
961
962 pub fn sub_query_document_query(
966 &self,
967 sub_query: &DriveSubQuery<'a>,
968 values: &[Identifier],
969 platform_version: &PlatformVersion,
970 ) -> Result<DriveDocumentQuery<'a>, Error> {
971 let direction = self.page_direction(platform_version)?;
972 self.sub_query_document_query_with_direction(sub_query, values, direction, platform_version)
973 }
974
975 pub(crate) fn sub_query_document_query_with_direction(
979 &self,
980 sub_query: &DriveSubQuery<'a>,
981 values: &[Identifier],
982 direction: bool,
983 platform_version: &PlatformVersion,
984 ) -> Result<DriveDocumentQuery<'a>, Error> {
985 let ids = sorted_values(values);
986 let in_value = || {
987 Value::Array(
988 ids.iter()
989 .map(|id| Value::Identifier(id.to_buffer()))
990 .collect(),
991 )
992 };
993
994 if sub_query.is_by_id_join() {
995 if !sub_query.where_clauses.is_empty() {
996 return Err(unsupported(
997 "a by-id join takes no fixed clauses: every derived id must resolve"
998 .to_string(),
999 ));
1000 }
1001 return Ok(DriveDocumentQuery {
1002 contract: sub_query.contract,
1003 document_type: sub_query.document_type,
1004 internal_clauses: InternalClauses {
1005 primary_key_in_clause: Some(WhereClause {
1006 field: dpp::document::property_names::ID.to_string(),
1007 operator: WhereOperator::In,
1008 value: in_value(),
1009 }),
1010 primary_key_equal_clause: None,
1011 in_clauses: Vec::new(),
1012 range_clause: None,
1013 equal_clauses: Default::default(),
1014 },
1015 offset: None,
1016 limit: None,
1017 order_by: Default::default(),
1018 start_at: None,
1019 start_at_included: false,
1020 block_time_ms: None,
1021 resolved_time_ranges: Vec::new(),
1022 sub_queries: Vec::new(),
1023 });
1024 }
1025
1026 let mut clauses = sub_query.where_clauses.clone();
1027 let mut order_by: indexmap::IndexMap<String, OrderClause> = sub_query
1028 .order_by
1029 .iter()
1030 .map(|clause| (clause.field.clone(), clause.clone()))
1031 .collect();
1032 if let Some(binding) = &sub_query.binding {
1033 clauses.push(WhereClause {
1034 field: binding.field.clone(),
1035 operator: WhereOperator::In,
1036 value: in_value(),
1037 });
1038 if !order_by.contains_key(&binding.field) {
1047 order_by.insert(
1048 binding.field.clone(),
1049 OrderClause {
1050 field: binding.field.clone(),
1051 ascending: direction,
1052 },
1053 );
1054 }
1055 }
1056 Ok(DriveDocumentQuery {
1057 contract: sub_query.contract,
1058 document_type: sub_query.document_type,
1059 internal_clauses: InternalClauses::extract_from_clauses(clauses, platform_version)?,
1060 offset: None,
1061 limit: sub_query.limit,
1062 order_by,
1063 start_at: None,
1064 start_at_included: false,
1065 block_time_ms: None,
1066 resolved_time_ranges: Vec::new(),
1067 sub_queries: Vec::new(),
1068 })
1069 }
1070
1071 pub fn sub_query_count_query<'b>(
1075 &'b self,
1076 sub_query: &'b DriveSubQuery<'a>,
1077 values: &[Identifier],
1078 _platform_version: &PlatformVersion,
1079 ) -> Result<DriveDocumentCountQuery<'b>, Error> {
1080 let Some(binding) = &sub_query.binding else {
1081 return Err(unsupported("a count sub-query must be bound".to_string()));
1082 };
1083 let mut where_clauses = sub_query.where_clauses.clone();
1084 where_clauses.push(WhereClause {
1085 field: binding.field.clone(),
1086 operator: WhereOperator::In,
1087 value: Value::Array(
1088 sorted_values(values)
1089 .into_iter()
1090 .map(|id| Value::Identifier(id.to_buffer()))
1091 .collect(),
1092 ),
1093 });
1094 let index = DriveDocumentCountQuery::find_countable_index_for_where_clauses(
1095 sub_query.document_type.indexes(),
1096 &where_clauses,
1097 &[],
1098 )
1099 .ok_or_else(|| {
1100 unsupported(format!(
1101 "count sub-query on \"{}\" needs a `countable: true` index covering its fixed \
1102 clauses and the bound field \"{}\"",
1103 sub_query.document_type.name(),
1104 binding.field,
1105 ))
1106 })?;
1107 Ok(DriveDocumentCountQuery {
1108 document_type: sub_query.document_type,
1109 contract_id: sub_query.contract.id().to_buffer(),
1110 document_type_name: sub_query.document_type.name().to_string(),
1111 index,
1112 where_clauses,
1113 })
1114 }
1115
1116 pub fn sub_query_path_query(
1118 &self,
1119 sub_query: &DriveSubQuery<'a>,
1120 values: &[Identifier],
1121 platform_version: &PlatformVersion,
1122 ) -> Result<PathQuery, Error> {
1123 let direction = self.page_direction(platform_version)?;
1124 self.sub_query_path_query_with_direction(sub_query, values, direction, platform_version)
1125 }
1126
1127 fn sub_query_path_query_with_direction(
1128 &self,
1129 sub_query: &DriveSubQuery<'a>,
1130 values: &[Identifier],
1131 direction: bool,
1132 platform_version: &PlatformVersion,
1133 ) -> Result<PathQuery, Error> {
1134 let path_query = match sub_query.kind {
1135 SubQueryKind::Documents => self
1136 .sub_query_document_query_with_direction(
1137 sub_query,
1138 values,
1139 direction,
1140 platform_version,
1141 )?
1142 .construct_path_query(None, platform_version)?,
1143 SubQueryKind::Count => self
1144 .sub_query_count_query(sub_query, values, platform_version)?
1145 .point_lookup_count_path_query(platform_version)?,
1146 };
1147 Ok(Self::budget_as_instance_cap(path_query))
1148 }
1149
1150 fn page_direction(&self, platform_version: &PlatformVersion) -> Result<bool, Error> {
1153 Ok(self
1154 .page_path_query(platform_version)?
1155 .query
1156 .query
1157 .left_to_right)
1158 }
1159
1160 pub(crate) fn sub_query_proof_path_query(
1165 &self,
1166 sub_query: &DriveSubQuery<'a>,
1167 values: &[Identifier],
1168 direction: bool,
1169 platform_version: &PlatformVersion,
1170 ) -> Result<PathQuery, Error> {
1171 let mut path_query = self.sub_query_path_query_with_direction(
1172 sub_query,
1173 values,
1174 direction,
1175 platform_version,
1176 )?;
1177 if sub_query.kind == SubQueryKind::Documents
1178 && !sub_query.is_by_id_join()
1179 && path_query.query.query.left_to_right != direction
1180 {
1181 return Err(unsupported(if sub_query.binding.is_none() {
1182 "a sibling sub-query's ordering must match the page's direction; order it \
1183 explicitly by its index property, in the page's direction"
1184 .to_string()
1185 } else {
1186 "a documents sub-query's outer ordering must match the page's direction; \
1187 changing it for the merged proof would change its result"
1188 .to_string()
1189 }));
1190 }
1191 path_query.query.query.left_to_right = direction;
1194 Ok(path_query)
1195 }
1196
1197 pub fn proof_path_queries(
1206 &self,
1207 derived: &[DerivedValues],
1208 platform_version: &PlatformVersion,
1209 ) -> Result<(PathQuery, Vec<Option<PathQuery>>), Error> {
1210 if derived.len() != self.sub_queries.len() {
1211 return Err(Error::Drive(DriveError::CorruptedCodeExecution(
1212 "one derived value list per sub-query",
1213 )));
1214 }
1215 let page = self.page_path_query(platform_version)?;
1216 let direction = page.query.query.left_to_right;
1217 let mut sub_path_queries = Vec::with_capacity(self.sub_queries.len());
1218 for (sub_query, values) in self.sub_queries.iter().zip(derived) {
1219 if sub_query.binding.is_some() && values.is_empty() {
1220 sub_path_queries.push(None);
1221 continue;
1222 }
1223 let path_query =
1224 self.sub_query_proof_path_query(sub_query, values, direction, platform_version)?;
1225 sub_path_queries.push(Some(path_query));
1226 }
1227 let mut count_terminal_paths = BTreeSet::new();
1232 for (sub_query, path_query) in self.sub_queries.iter().zip(&sub_path_queries) {
1233 if sub_query.kind != SubQueryKind::Count {
1234 continue;
1235 }
1236 if let Some(path_query) = path_query {
1237 for (mut path, key) in path_query
1238 .terminal_keys(MAX_BOUND_VALUES, &platform_version.drive.grove_version)?
1239 {
1240 path.push(key);
1241 count_terminal_paths.insert(path);
1242 }
1243 }
1244 }
1245 for terminal_path in count_terminal_paths {
1246 for component in std::iter::once(&page).chain(sub_path_queries.iter().flatten()) {
1247 if !terminal_path.starts_with(&component.path)
1251 && !component.path.starts_with(&terminal_path)
1252 {
1253 continue;
1254 }
1255 if Self::path_query_descends_through(component, &terminal_path, platform_version)? {
1256 return Err(unsupported(
1257 "a count sub-query selects a tree another component descends through; \
1258 split them into separate requests"
1259 .to_string(),
1260 ));
1261 }
1262 }
1263 }
1264 let documents: Vec<&PathQuery> = std::iter::once(&page)
1271 .chain(
1272 sub_path_queries
1273 .iter()
1274 .zip(&self.sub_queries)
1275 .filter(|(_, sub_query)| sub_query.kind == SubQueryKind::Documents)
1276 .filter_map(|(path_query, _)| path_query.as_ref()),
1277 )
1278 .collect();
1279 for deeper in &documents {
1280 for shallower in &documents {
1281 if deeper.path.len() <= shallower.path.len()
1282 || !deeper.path.starts_with(&shallower.path)
1283 {
1284 continue;
1285 }
1286 if Self::path_query_descends_through(shallower, &deeper.path, platform_version)? {
1287 return Err(unsupported(
1288 "a documents sub-query walks through another documents component's \
1289 subtree, so their rows could not be told apart; split them into \
1290 separate requests"
1291 .to_string(),
1292 ));
1293 }
1294 }
1295 }
1296 Ok((page, sub_path_queries))
1297 }
1298
1299 fn path_query_descends_through(
1303 query: &PathQuery,
1304 terminal_path: &[Vec<u8>],
1305 platform_version: &PlatformVersion,
1306 ) -> Result<bool, Error> {
1307 let mut prefix = Vec::with_capacity(terminal_path.len());
1308 for key in terminal_path {
1309 let Some(selection) =
1310 query.query_items_at_path(&prefix, &platform_version.drive.grove_version)?
1311 else {
1312 return Ok(false);
1313 };
1314 if !selection.items.iter().any(|item| item.contains(key))
1315 || !selection.has_subquery_or_matching_in_path_on_key(key)
1316 {
1317 return Ok(false);
1318 }
1319 prefix.push(key.as_slice());
1320 }
1321 Ok(true)
1322 }
1323
1324 pub fn merged_path_query(
1330 page: &PathQuery,
1331 sub_path_queries: &[Option<PathQuery>],
1332 platform_version: &PlatformVersion,
1333 ) -> Result<PathQuery, Error> {
1334 let mut components: Vec<&PathQuery> = vec![page];
1335 components.extend(sub_path_queries.iter().flatten());
1336 if components.len() == 1 {
1337 return Ok(page.clone());
1338 }
1339 PathQuery::merge(components, &platform_version.drive.grove_version)
1340 .map_err(merge_error_to_shape_error)
1341 }
1342
1343 pub(crate) fn decode_document_trios(
1347 query: &DriveDocumentQuery<'a>,
1348 trios: Vec<PresentTrio>,
1349 platform_version: &PlatformVersion,
1350 ) -> Result<Vec<Document>, Error> {
1351 if query.document_type.index_only() {
1352 let index = query.index_only_query_index(platform_version)?;
1353 return trios
1354 .into_iter()
1355 .map(|(path, key, _)| {
1356 synthesize_index_only_document(
1357 query.contract.id(),
1358 query.document_type,
1359 index,
1360 &path,
1361 &key,
1362 )
1363 })
1364 .collect();
1365 }
1366 trios
1367 .into_iter()
1368 .map(|(_, _, element)| {
1369 let serialized = element.into_item_bytes().map_err(Error::from)?;
1370 Document::from_bytes(serialized.as_slice(), query.document_type, platform_version)
1371 .map_err(|e| Error::Protocol(Box::new(e)))
1372 })
1373 .collect()
1374 }
1375
1376 pub(crate) fn decode_sub_query_document_trios(
1380 &self,
1381 sub_query: &DriveSubQuery<'a>,
1382 values: &[Identifier],
1383 direction: bool,
1384 trios: Vec<PresentTrio>,
1385 platform_version: &PlatformVersion,
1386 ) -> Result<Vec<Document>, Error> {
1387 let query = self.sub_query_document_query_with_direction(
1388 sub_query,
1389 values,
1390 direction,
1391 platform_version,
1392 )?;
1393 let documents = Self::decode_document_trios(&query, trios, platform_version)?;
1394 self.assemble_documents(sub_query, values, &documents)
1395 }
1396
1397 fn decode_count_trios(base_path_len: usize, trios: Vec<PresentTrio>) -> Vec<SplitCountEntry> {
1403 let mut entries = point_lookup_count_entries(
1405 base_path_len,
1406 true,
1407 trios
1408 .into_iter()
1409 .map(|(path, key, element)| (path, key, Some(element))),
1410 );
1411 entries.sort_by(|a, b| a.key.cmp(&b.key));
1414 entries
1415 }
1416
1417 fn assemble_documents(
1423 &self,
1424 sub_query: &DriveSubQuery<'a>,
1425 values: &[Identifier],
1426 documents: &[Document],
1427 ) -> Result<Vec<Document>, Error> {
1428 let Some(binding) = &sub_query.binding else {
1429 return Ok(documents.to_vec());
1430 };
1431 let admitted: BTreeSet<Identifier> = values.iter().copied().collect();
1432 if sub_query.is_by_id_join() {
1433 let mut by_id: BTreeMap<Identifier, &Document> = BTreeMap::new();
1434 for document in documents {
1435 let id = document.id();
1436 if !admitted.contains(&id) {
1437 continue;
1439 }
1440 if by_id.insert(id, document).is_some() {
1441 return Err(corrupted_proof(format!(
1442 "composite join results carry document {} twice",
1443 id
1444 )));
1445 }
1446 }
1447 let mut ordered = Vec::with_capacity(values.len());
1448 for value in values {
1449 let document = by_id.remove(value).ok_or_else(|| {
1450 corrupted_proof(format!(
1451 "composite join results are missing referenced document {}: a \
1452 permanentDocument reference cannot dangle, so the proof does not \
1453 cover the derived query",
1454 value
1455 ))
1456 })?;
1457 ordered.push(document.clone());
1458 }
1459 return Ok(ordered);
1460 }
1461 let mut mine = Vec::new();
1462 for document in documents {
1463 match document_bound_value(document, &binding.field)? {
1464 Some(value) if admitted.contains(&value) => mine.push(document.clone()),
1465 _ => {}
1466 }
1467 }
1468 Ok(mine)
1469 }
1470
1471 fn assemble_counts(
1474 values: &[Identifier],
1475 entries: Vec<SplitCountEntry>,
1476 ) -> Result<Vec<SplitCountEntry>, Error> {
1477 let admitted: BTreeSet<Identifier> = values.iter().copied().collect();
1478 let mut mine = Vec::with_capacity(entries.len());
1479 for entry in entries {
1480 let Ok(value) = Identifier::from_bytes(&entry.key) else {
1481 return Err(corrupted_proof(
1482 "a composite count entry is keyed by something other than an identifier"
1483 .to_string(),
1484 ));
1485 };
1486 if admitted.contains(&value) {
1487 mine.push(entry);
1488 }
1489 }
1490 Ok(mine)
1491 }
1492
1493 pub(crate) fn assemble_from_trios(
1500 &self,
1501 derived: &[DerivedValues],
1502 page_path_query: &PathQuery,
1503 sub_path_queries: &[Option<PathQuery>],
1504 trios: Vec<ProvedTrio>,
1505 platform_version: &PlatformVersion,
1506 ) -> Result<CompositeDocumentsResult, Error> {
1507 let direction = page_path_query.query.query.left_to_right;
1512 let mut groups: Vec<(Vec<Vec<u8>>, Vec<Component>)> = Vec::new();
1513 let mut count_members_by_position: BTreeMap<_, Vec<usize>> = BTreeMap::new();
1514 let mut register = |path: &Vec<Vec<u8>>, component: Component| {
1515 if let Some((_, members)) = groups.iter_mut().find(|(p, _)| p == path) {
1516 members.push(component);
1517 } else {
1518 groups.push((path.clone(), vec![component]));
1519 }
1520 };
1521 register(&page_path_query.path, Component::Page);
1522 for (index, path_query) in sub_path_queries.iter().enumerate() {
1523 if let Some(path_query) = path_query {
1524 if self.sub_queries[index].kind == SubQueryKind::Count {
1525 for position in path_query
1526 .terminal_keys(MAX_BOUND_VALUES, &platform_version.drive.grove_version)?
1527 {
1528 count_members_by_position
1529 .entry(position)
1530 .or_default()
1531 .push(index);
1532 }
1533 } else {
1534 register(&path_query.path, Component::Sub(index));
1535 }
1536 }
1537 }
1538
1539 let mut trios_by_group: Vec<Vec<PresentTrio>> = vec![Vec::new(); groups.len()];
1542 let mut count_trios_by_sub: Vec<Vec<PresentTrio>> =
1543 vec![Vec::new(); self.sub_queries.len()];
1544 for (path, key, element) in trios {
1545 let Some(element) = element else {
1546 continue;
1547 };
1548 if !matches!(element, Element::Item(..)) {
1549 let position = (path, key);
1550 let members = count_members_by_position.get(&position).ok_or_else(|| {
1551 corrupted_proof(
1552 "the composite proof carries a count at a position no component \
1553 selected"
1554 .to_string(),
1555 )
1556 })?;
1557 let (last, others) = members.split_last().ok_or_else(|| {
1559 Error::Drive(DriveError::CorruptedCodeExecution(
1560 "a registered count position has at least one member",
1561 ))
1562 })?;
1563 for index in others {
1564 count_trios_by_sub[*index].push((
1565 position.0.clone(),
1566 position.1.clone(),
1567 element.clone(),
1568 ));
1569 }
1570 count_trios_by_sub[*last].push((position.0, position.1, element));
1571 continue;
1572 }
1573 let best = groups
1574 .iter()
1575 .enumerate()
1576 .filter(|(_, (base, _))| path.starts_with(base))
1577 .max_by_key(|(_, (base, _))| base.len())
1578 .map(|(index, _)| index)
1579 .ok_or_else(|| {
1580 corrupted_proof(
1581 "the composite proof proved an entry outside every component's \
1582 subtree"
1583 .to_string(),
1584 )
1585 })?;
1586 trios_by_group[best].push((path, key, element));
1587 }
1588
1589 let mut page_documents: Option<Vec<Document>> = None;
1592 let mut sub_results: Vec<Option<SubQueryResult>> = vec![None; self.sub_queries.len()];
1593 for ((_, documents_members), document_trios) in groups.iter().zip(trios_by_group) {
1594 let documents = match documents_members[0] {
1597 Component::Page => {
1598 Self::decode_document_trios(self, document_trios, platform_version)?
1599 }
1600 Component::Sub(index) => {
1601 let query = self.sub_query_document_query_with_direction(
1602 &self.sub_queries[index],
1603 &derived[index],
1604 direction,
1605 platform_version,
1606 )?;
1607 Self::decode_document_trios(&query, document_trios, platform_version)?
1608 }
1609 };
1610 let mut claimed: BTreeSet<usize> = BTreeSet::new();
1611 for member in documents_members {
1612 match member {
1613 Component::Page => {
1614 let page_ids: Option<BTreeSet<Identifier>> = if documents_members.len() > 1
1615 {
1616 Some(self.page_ids()?)
1617 } else {
1618 None
1619 };
1620 let mut mine = Vec::new();
1621 for (position, document) in documents.iter().enumerate() {
1622 let is_mine = page_ids
1623 .as_ref()
1624 .is_none_or(|ids| ids.contains(&document.id()));
1625 if is_mine {
1626 claimed.insert(position);
1627 mine.push(document.clone());
1628 }
1629 }
1630 page_documents = Some(mine);
1631 }
1632 Component::Sub(index) => {
1633 let sub_query = &self.sub_queries[*index];
1634 let mine =
1635 self.assemble_documents(sub_query, &derived[*index], &documents)?;
1636 let mine_ids: BTreeSet<Identifier> =
1637 mine.iter().map(|document| document.id()).collect();
1638 for (position, document) in documents.iter().enumerate() {
1639 if mine_ids.contains(&document.id()) {
1640 claimed.insert(position);
1641 }
1642 }
1643 sub_results[*index] = Some(SubQueryResult::Documents(mine));
1644 }
1645 }
1646 }
1647 if claimed.len() != documents.len() {
1648 return Err(corrupted_proof(
1649 "the composite proof carries a document that no component's \
1650 derivation asked for"
1651 .to_string(),
1652 ));
1653 }
1654 }
1655
1656 for (index, count_trios) in count_trios_by_sub.into_iter().enumerate() {
1657 if self.sub_queries[index].kind != SubQueryKind::Count {
1658 continue;
1659 }
1660 let Some(path_query) = &sub_path_queries[index] else {
1661 continue;
1662 };
1663 let entries = Self::decode_count_trios(path_query.path.len(), count_trios);
1664 sub_results[index] = Some(SubQueryResult::Counts(Self::assemble_counts(
1665 &derived[index],
1666 entries,
1667 )?));
1668 }
1669
1670 Ok(CompositeDocumentsResult {
1671 page_documents: page_documents.unwrap_or_default(),
1672 sub_results: sub_results
1673 .into_iter()
1674 .zip(&self.sub_queries)
1675 .map(|(result, sub_query)| {
1676 result.unwrap_or_else(|| match sub_query.kind {
1677 SubQueryKind::Documents => SubQueryResult::Documents(Vec::new()),
1678 SubQueryKind::Count => SubQueryResult::Counts(Vec::new()),
1679 })
1680 })
1681 .collect(),
1682 })
1683 }
1684
1685 fn page_ids(&self) -> Result<BTreeSet<Identifier>, Error> {
1689 let mut ids = BTreeSet::new();
1690 if let Some(clause) = &self.internal_clauses.primary_key_equal_clause {
1691 ids.insert(clause.value.to_identifier().map_err(|_| {
1692 Error::Drive(DriveError::CorruptedCodeExecution(
1693 "a primary-key equality clause holds an identifier",
1694 ))
1695 })?);
1696 }
1697 if let Some(clause) = &self.internal_clauses.primary_key_in_clause {
1698 for value in clause
1699 .in_values()
1700 .into_data()
1701 .map_err(|_| {
1702 Error::Drive(DriveError::CorruptedCodeExecution(
1703 "a primary-key in clause holds an array",
1704 ))
1705 })?
1706 .iter()
1707 {
1708 ids.insert(value.to_identifier().map_err(|_| {
1709 Error::Drive(DriveError::CorruptedCodeExecution(
1710 "a primary-key in clause holds identifiers",
1711 ))
1712 })?);
1713 }
1714 }
1715 Ok(ids)
1716 }
1717
1718 pub(crate) fn derive_for<'d>(
1727 &self,
1728 sub_query: &DriveSubQuery<'a>,
1729 page_documents: &[Document],
1730 sub_documents: impl Fn(usize) -> Option<&'d [Document]>,
1731 ) -> Result<DerivedValues, Error> {
1732 let Some(binding) = &sub_query.binding else {
1733 return Ok(Vec::new());
1734 };
1735 match binding.source {
1736 BindingSource::Page => self.derive_values(binding, page_documents),
1737 BindingSource::SubQuery(source_index) => {
1738 let documents = sub_documents(source_index).ok_or_else(|| {
1739 Error::Drive(DriveError::CorruptedCodeExecution(
1740 "a binding's source sub-query was not materialized before it",
1741 ))
1742 })?;
1743 self.derive_values(binding, documents)
1744 }
1745 }
1746 }
1747
1748 pub fn derive_all<'d>(
1751 &self,
1752 page_documents: &[Document],
1753 sub_documents: impl Fn(usize) -> Option<&'d [Document]>,
1754 ) -> Result<Vec<DerivedValues>, Error> {
1755 self.sub_queries
1756 .iter()
1757 .map(|sub_query| self.derive_for(sub_query, page_documents, &sub_documents))
1758 .collect()
1759 }
1760
1761 pub(crate) fn is_binding_source(&self, index: usize) -> bool {
1763 self.sub_queries.iter().any(|sub_query| {
1764 matches!(
1765 sub_query.binding,
1766 Some(SubQueryBinding {
1767 source: BindingSource::SubQuery(source),
1768 ..
1769 }) if source == index
1770 )
1771 })
1772 }
1773}
1774
1775#[cfg(feature = "server")]
1779fn is_absent_path(error: &Error) -> bool {
1780 matches!(
1781 error,
1782 Error::GroveDB(e) if matches!(
1783 e.as_ref(),
1784 grovedb::Error::PathKeyNotFound(_)
1785 | grovedb::Error::PathNotFound(_)
1786 | grovedb::Error::PathParentLayerNotFound(_)
1787 )
1788 )
1789}
1790
1791#[cfg(feature = "server")]
1792impl<'a> DriveDocumentQuery<'a> {
1793 fn materialize_component(
1804 query: &DriveDocumentQuery<'a>,
1805 path_query: &PathQuery,
1806 drive: &crate::drive::Drive,
1807 transaction: grovedb::TransactionArg,
1808 drive_operations: &mut Vec<crate::fees::op::LowLevelDriveOperation>,
1809 platform_version: &PlatformVersion,
1810 ) -> Result<Vec<Document>, Error> {
1811 use grovedb::query_result_type::QueryResultType;
1812
1813 if query.document_type.index_only() {
1814 let results = match drive.grove_get_path_query(
1815 path_query,
1816 transaction,
1817 QueryResultType::QueryPathKeyElementTrioResultType,
1818 drive_operations,
1819 &platform_version.drive,
1820 ) {
1821 Err(error) if is_absent_path(&error) => return Ok(Vec::new()),
1822 other => other?.0,
1823 };
1824 return Self::decode_document_trios(
1825 query,
1826 results.to_path_key_elements(),
1827 platform_version,
1828 );
1829 }
1830 let serialized = match drive.grove_get_path_query_serialized_results(
1833 path_query,
1834 transaction,
1835 drive_operations,
1836 &platform_version.drive,
1837 ) {
1838 Err(error) if is_absent_path(&error) => return Ok(Vec::new()),
1839 other => other?.0,
1840 };
1841 serialized
1842 .into_iter()
1843 .map(|bytes| {
1844 Document::from_bytes(bytes.as_slice(), query.document_type, platform_version)
1845 .map_err(|e| Error::Protocol(Box::new(e)))
1846 })
1847 .collect()
1848 }
1849
1850 #[allow(clippy::too_many_arguments)]
1854 fn materialize_sub_result(
1855 &self,
1856 sub_query: &DriveSubQuery<'a>,
1857 values: &[Identifier],
1858 direction: bool,
1859 drive: &crate::drive::Drive,
1860 transaction: grovedb::TransactionArg,
1861 drive_operations: &mut Vec<crate::fees::op::LowLevelDriveOperation>,
1862 platform_version: &PlatformVersion,
1863 ) -> Result<SubQueryResult, Error> {
1864 use grovedb::query_result_type::{QueryResultElement, QueryResultType};
1865
1866 if sub_query.binding.is_some() && values.is_empty() {
1867 return Ok(match sub_query.kind {
1868 SubQueryKind::Documents => SubQueryResult::Documents(Vec::new()),
1869 SubQueryKind::Count => SubQueryResult::Counts(Vec::new()),
1870 });
1871 }
1872 match sub_query.kind {
1873 SubQueryKind::Documents => {
1874 let query = self.sub_query_document_query_with_direction(
1875 sub_query,
1876 values,
1877 direction,
1878 platform_version,
1879 )?;
1880 let path_query = self.sub_query_proof_path_query(
1881 sub_query,
1882 values,
1883 direction,
1884 platform_version,
1885 )?;
1886 let documents = Self::materialize_component(
1887 &query,
1888 &path_query,
1889 drive,
1890 transaction,
1891 drive_operations,
1892 platform_version,
1893 )?;
1894 Ok(SubQueryResult::Documents(
1895 self.assemble_documents(sub_query, values, &documents)?,
1896 ))
1897 }
1898 SubQueryKind::Count => {
1899 let path_query = self
1900 .sub_query_count_query(sub_query, values, platform_version)?
1901 .point_lookup_count_path_query(platform_version)?;
1902 let base_path_len = path_query.path.len();
1903 let (results, _skipped) = match drive.grove_get_path_query(
1904 &path_query,
1905 transaction,
1906 QueryResultType::QueryPathKeyElementTrioResultType,
1907 drive_operations,
1908 &platform_version.drive,
1909 ) {
1910 Err(Error::GroveDB(e))
1912 if matches!(
1913 e.as_ref(),
1914 grovedb::Error::PathKeyNotFound(_)
1915 | grovedb::Error::PathNotFound(_)
1916 | grovedb::Error::PathParentLayerNotFound(_)
1917 ) =>
1918 {
1919 return Ok(SubQueryResult::Counts(Vec::new()));
1920 }
1921 other => other?,
1922 };
1923 let trios = results
1924 .elements
1925 .into_iter()
1926 .filter_map(|element| match element {
1927 QueryResultElement::PathKeyElementTrioResultItem(trio) => Some(trio),
1928 _ => None,
1929 })
1930 .collect();
1931 let entries = Self::decode_count_trios(base_path_len, trios);
1932 Ok(SubQueryResult::Counts(Self::assemble_counts(
1933 values, entries,
1934 )?))
1935 }
1936 }
1937 }
1938
1939 pub(crate) fn execute_composite_no_proof_internal(
1941 &self,
1942 drive: &crate::drive::Drive,
1943 transaction: grovedb::TransactionArg,
1944 drive_operations: &mut Vec<crate::fees::op::LowLevelDriveOperation>,
1945 platform_version: &PlatformVersion,
1946 ) -> Result<CompositeDocumentsResult, Error> {
1947 self.validate_composite(platform_version)?;
1948
1949 let page_path_query = self.page_path_query(platform_version)?;
1950 let direction = page_path_query.query.query.left_to_right;
1951 let page_documents = Self::materialize_component(
1952 self,
1953 &page_path_query,
1954 drive,
1955 transaction,
1956 drive_operations,
1957 platform_version,
1958 )?;
1959 let mut sub_results: Vec<SubQueryResult> = Vec::with_capacity(self.sub_queries.len());
1960 let mut derived = Vec::with_capacity(self.sub_queries.len());
1961 for sub_query in &self.sub_queries {
1962 let values = self.derive_for(sub_query, &page_documents, |source| {
1963 sub_results.get(source).map(|result| result.documents())
1964 })?;
1965 sub_results.push(self.materialize_sub_result(
1966 sub_query,
1967 &values,
1968 direction,
1969 drive,
1970 transaction,
1971 drive_operations,
1972 platform_version,
1973 )?);
1974 derived.push(values);
1975 }
1976 self.proof_path_queries(&derived, platform_version)?;
1980 Ok(CompositeDocumentsResult {
1981 page_documents,
1982 sub_results,
1983 })
1984 }
1985
1986 pub(crate) fn execute_composite_with_proof_internal(
2003 &self,
2004 drive: &crate::drive::Drive,
2005 drive_operations: &mut Vec<crate::fees::op::LowLevelDriveOperation>,
2006 platform_version: &PlatformVersion,
2007 ) -> Result<(Vec<u8>, Vec<Document>), Error> {
2008 self.validate_composite(platform_version)?;
2009 let page_path_query = self.page_path_query(platform_version)?;
2010 let direction = page_path_query.query.query.left_to_right;
2011
2012 const MAX_ATTEMPTS: usize = 3;
2016 for _ in 0..MAX_ATTEMPTS {
2017 let operations_before = drive_operations.len();
2020 let root_before = drive
2021 .grove
2022 .root_hash(None, &platform_version.drive.grove_version)
2023 .unwrap()?;
2024
2025 let page_documents = Self::materialize_component(
2026 self,
2027 &page_path_query,
2028 drive,
2029 None,
2030 drive_operations,
2031 platform_version,
2032 )?;
2033 let mut derived: Vec<DerivedValues> = Vec::with_capacity(self.sub_queries.len());
2036 let mut materialized: Vec<Option<Vec<Document>>> = vec![None; self.sub_queries.len()];
2037 for (index, sub_query) in self.sub_queries.iter().enumerate() {
2038 let values = self.derive_for(sub_query, &page_documents, |source| {
2039 materialized
2040 .get(source)
2041 .and_then(|documents| documents.as_deref())
2042 })?;
2043 if self.is_binding_source(index) {
2044 let result = self.materialize_sub_result(
2045 sub_query,
2046 &values,
2047 direction,
2048 drive,
2049 None,
2050 drive_operations,
2051 platform_version,
2052 )?;
2053 materialized[index] = Some(result.documents().to_vec());
2054 }
2055 derived.push(values);
2056 }
2057
2058 let (page_path_query, sub_path_queries) =
2059 self.proof_path_queries(&derived, platform_version)?;
2060 let merged_query =
2063 Self::merged_path_query(&page_path_query, &sub_path_queries, platform_version)?;
2064 let proof = drive
2065 .grove
2066 .prove_query(&merged_query, None, &platform_version.drive.grove_version)
2067 .unwrap()?;
2068
2069 let root_after = drive
2070 .grove
2071 .root_hash(None, &platform_version.drive.grove_version)
2072 .unwrap()?;
2073 if root_before != root_after {
2074 drive_operations.truncate(operations_before);
2075 continue;
2076 }
2077 return Ok((proof, page_documents));
2078 }
2079 Err(Error::Drive(DriveError::NotSupported(
2080 "composite proof generation raced a block commit on every attempt; transient — \
2081 retry the request",
2082 )))
2083 }
2084}
2085
2086#[cfg(test)]
2087mod tests {
2088 use super::*;
2089 use grovedb::{Query, SizedQuery, SubqueryBranch};
2090
2091 #[test]
2096 fn should_follow_a_walk_through_selected_keys_and_subqueries_only() {
2097 let pv = PlatformVersion::latest();
2098 let key = |name: &str| name.as_bytes().to_vec();
2099 let mut body = Query::new();
2100 body.insert_key(key("x"));
2101 body.default_subquery_branch = SubqueryBranch {
2102 subquery_path: Some(vec![key("c")]),
2103 subquery: Some(Box::new(Query::new_range_full())),
2104 };
2105 let shallower = PathQuery::new(vec![key("a"), key("b")], SizedQuery::new(body, None, None));
2106 let descends = |path: &[&str]| {
2107 DriveDocumentQuery::path_query_descends_through(
2108 &shallower,
2109 &path.iter().map(|segment| key(segment)).collect::<Vec<_>>(),
2110 pv,
2111 )
2112 .expect("the walk resolves")
2113 };
2114 assert!(
2115 descends(&["a", "b", "x", "c"]),
2116 "selected key, then its subquery path"
2117 );
2118 assert!(!descends(&["a", "b", "x", "d"]), "not the subquery path");
2119 assert!(!descends(&["a", "b", "y", "c"]), "an unselected key");
2120 assert!(!descends(&["a", "z"]), "off the base path");
2121 assert!(
2122 !descends(&["a", "b", "x", "c", "k"]),
2123 "past the walk's leaves"
2124 );
2125 }
2126}