1#[cfg(any(feature = "server", feature = "verify"))]
2use dpp::data_contract::document_type::typed_array_property;
3#[cfg(feature = "server")]
4use dpp::data_contract::document_type::DocumentPropertyType;
5use dpp::data_contract::document_type::{
6 IndexBucketing, IntegerRangeTransform, TimeRangeTransform,
7};
8use std::sync::Arc;
9
10#[cfg(any(feature = "server", feature = "verify"))]
11pub use {
12 chained_document_query::{
16 ChainedDocumentsResult, ChainedOuterDocuments, MAX_CHAINED_JOIN_VALUES,
17 },
18 composite_document_query::{
22 BindingSource, CompositeDocumentsResult, DriveSubQuery, SubQueryBinding, SubQueryKind,
23 SubQueryResult, MAX_BOUND_VALUES, MAX_SUB_QUERIES,
24 },
25 conditions::{ValueClause, WhereClause, WhereOperator},
26 drive_document_average_query::{AverageEntry, AverageMode},
31 drive_document_count_query::{
38 CountMode, DocumentCountMode, DriveDocumentCountQuery, SplitCountEntry,
39 },
40 drive_document_having_query::{
46 AxisRangeBounds, DocumentHavingMode, DriveDocumentHavingQuery, MAX_HAVING_LIMIT,
47 },
48 drive_document_ranked_query::{
54 DocumentRankedMode, DriveDocumentRankedQuery, RankedAxis, RankedEntry, RankedEntryValue,
55 RankedPage, RankedPaginationInputs, MAX_RANKED_LIMIT, RANKED_AVG_SCALE,
56 RANKED_COUNT_ORDER_KEY,
57 },
58 drive_document_sum_query::{DriveDocumentSumQuery, SumEntry, SumMode},
63 grovedb::{PathQuery, Query, QueryItem, SizedQuery},
64 having::{
65 HavingAggregate, HavingAggregateFunction, HavingClause, HavingOperator, HavingRightOperand,
66 },
67 ordering::OrderClause,
68 projection::{SelectFunction, SelectProjection},
69 single_document_drive_query::SingleDocumentDriveQuery,
70 single_document_drive_query::SingleDocumentDriveQueryContestedStatus,
71 vote_polls_by_end_date_query::VotePollsByEndDateDriveQuery,
72 vote_query::IdentityBasedVoteDriveQuery,
73};
74
75#[cfg(feature = "server")]
78pub use drive_document_count_query::{
79 DocumentCountRequest, DocumentCountResponse, RangeCountOptions, MAX_LIMIT_AS_FAILSAFE,
80};
81
82#[cfg(feature = "server")]
86pub use drive_document_sum_query::{
87 DocumentSumRequest, DocumentSumResponse, RangeSumOptions, RangeSumWalkMode,
88};
89
90#[cfg(feature = "server")]
94pub use drive_document_average_query::{DocumentAverageRequest, DocumentAverageResponse};
95
96#[cfg(feature = "server")]
101pub use drive_document_ranked_query::{DocumentRankedRequest, DocumentRankedResponse};
102
103#[cfg(feature = "server")]
108pub use drive_document_having_query::{DocumentHavingRequest, DocumentHavingResponse};
109#[cfg(any(feature = "server", feature = "verify"))]
111use {
112 crate::{
113 drive::contract::paths::DataContractPaths,
114 error::{drive::DriveError, query::QuerySyntaxError, Error},
115 },
116 dpp::{
117 data_contract::{
118 accessors::v0::DataContractV0Getters,
119 document_type::{accessors::DocumentTypeV0Getters, methods::DocumentTypeV0Methods},
120 document_type::{DocumentTypeRef, Index},
121 DataContract,
122 },
123 document::{document_methods::DocumentMethodsV0, Document},
124 platform_value::{btreemap_extensions::BTreeValueRemoveFromMapHelper, Value},
125 version::PlatformVersion,
126 ProtocolError,
127 },
128 indexmap::IndexMap,
129 sqlparser::{
130 ast::{self, OrderByExpr, Select, Statement, TableFactor::Table, Value::Number},
131 dialect::MySqlDialect,
132 parser::Parser,
133 },
134 std::{
135 collections::{BTreeMap, HashSet},
136 ops::BitXor,
137 },
138};
139
140#[cfg(all(feature = "server", feature = "verify"))]
141use crate::verify::RootHash;
142
143#[cfg(any(feature = "server", feature = "verify"))]
144use crate::drive::document::ranked_index_tree_type::property_name_tree_type_and_ranked_axes_for_level;
145#[cfg(any(feature = "server", feature = "verify"))]
146use dpp::data_contract::document_type::accessors::DocumentTypeV2Getters;
147#[cfg(feature = "server")]
148use dpp::document::serialization_traits::DocumentPlatformConversionMethodsV0;
149#[cfg(any(feature = "server", feature = "verify"))]
150use dpp::document::DocumentV0Getters;
151#[cfg(feature = "server")]
152pub use grovedb::{
153 query_result_type::{QueryResultElements, QueryResultType},
154 Element, Error as GroveError, TransactionArg,
155};
156
157use dpp::document;
158use dpp::prelude::Identifier;
159use dpp::validation::{SimpleValidationResult, ValidationResult};
160#[cfg(feature = "server")]
161use {
162 crate::{drive::Drive, fees::op::LowLevelDriveOperation},
163 dpp::block::block_info::BlockInfo,
164};
165use crate::config::DriveConfig;
167use crate::util::common::encode::encode_u64;
169#[cfg(feature = "server")]
170use crate::util::grove_operations::QueryType::StatefulQuery;
171#[cfg(feature = "server")]
172use dpp::version::FeatureVersion;
173
174#[cfg(any(feature = "server", feature = "verify"))]
176pub mod canonicalize;
177#[cfg(any(feature = "server", feature = "verify"))]
178pub use canonicalize::{key_array_element_clauses, validate_and_canonicalize_where_clauses};
179#[cfg(any(feature = "server", feature = "verify"))]
180pub mod conditions;
181#[cfg(any(feature = "server", feature = "verify"))]
182mod defaults;
183#[cfg(any(feature = "server", feature = "verify"))]
184pub mod having;
185mod non_primary_key_path_query;
186#[cfg(any(feature = "server", feature = "verify"))]
187pub mod ordering;
188#[cfg(any(feature = "server", feature = "verify"))]
189pub mod projection;
190#[cfg(any(feature = "server", feature = "verify"))]
191mod single_document_drive_query;
192pub(crate) mod where_clause_grouping;
194
195#[cfg(feature = "server")]
197mod test_index;
198
199#[cfg(any(feature = "server", feature = "verify"))]
200pub mod vote_poll_vote_state_query;
202#[cfg(any(feature = "server", feature = "verify"))]
203pub mod vote_query;
205
206#[cfg(any(feature = "server", feature = "verify"))]
207pub mod vote_poll_contestant_votes_query;
209
210#[cfg(any(feature = "server", feature = "verify"))]
211pub mod vote_polls_by_end_date_query;
213
214#[cfg(any(feature = "server", feature = "verify"))]
215pub mod vote_polls_by_document_type_query;
217
218#[cfg(any(feature = "server", feature = "verify"))]
220pub mod yes_no_vote_poll_state_query;
221
222#[cfg(any(feature = "server", feature = "verify"))]
224pub mod masternode_yes_no_vote_query;
225
226#[cfg(any(feature = "server", feature = "verify"))]
232pub type ContractLookupFn<'a> =
233 dyn Fn(&Identifier) -> Result<Option<Arc<DataContract>>, Error> + 'a;
234
235#[cfg(any(feature = "server", feature = "verify"))]
246pub fn contract_lookup_fn_for_contract<'a>(
247 data_contract: Arc<DataContract>,
248) -> Box<ContractLookupFn<'a>> {
249 let func = move |id: &Identifier| -> Result<Option<Arc<DataContract>>, Error> {
250 if data_contract.id().ne(id) {
251 return Ok(None);
252 }
253 Ok(Some(Arc::clone(&data_contract)))
254 };
255 Box::new(func)
256}
257
258#[cfg(any(feature = "server", feature = "verify"))]
260pub mod contested_resource_votes_given_by_identity_query;
261#[cfg(any(feature = "server", feature = "verify"))]
263pub mod drive_contested_document_query;
264
265#[cfg(any(feature = "server", feature = "verify"))]
267pub mod proposer_block_count_query;
268
269#[cfg(any(feature = "server", feature = "verify"))]
271pub mod identity_token_balance_drive_query;
272#[cfg(any(feature = "server", feature = "verify"))]
274pub mod identity_token_info_drive_query;
275
276#[cfg(any(feature = "server", feature = "verify"))]
278pub mod filter;
279#[cfg(any(feature = "server", feature = "verify"))]
281pub mod token_status_drive_query;
282
283#[cfg(any(feature = "server", feature = "verify"))]
285pub mod drive_document_count_query;
286
287#[cfg(any(feature = "server", feature = "verify"))]
293pub mod drive_document_sum_query;
294
295#[cfg(any(feature = "server", feature = "verify"))]
302pub mod drive_document_average_query;
303
304#[cfg(any(feature = "server", feature = "verify"))]
310pub mod drive_document_having_query;
311
312#[cfg(any(feature = "server", feature = "verify"))]
319pub mod drive_document_ranked_query;
320
321#[cfg(any(feature = "server", feature = "verify"))]
326pub(crate) mod index_only_synthesis;
327
328#[cfg(any(feature = "server", feature = "verify"))]
334pub mod chained_document_query;
335
336#[cfg(any(feature = "server", feature = "verify"))]
340pub mod moderated_join;
341
342#[cfg(any(feature = "server", feature = "verify"))]
346pub mod composite_document_query;
347
348#[cfg(feature = "server")]
353pub mod drive_document_count_and_sum_query;
354
355pub type QuerySyntaxValidationResult<TData> = ValidationResult<TData, QuerySyntaxError>;
357
358pub type QuerySyntaxSimpleValidationResult = SimpleValidationResult<QuerySyntaxError>;
360
361#[cfg(any(feature = "server", feature = "verify"))]
362#[derive(Debug, Clone)]
369pub struct StartAtDocument<'a> {
370 pub document: Document,
372
373 pub document_type: DocumentTypeRef<'a>,
375
376 pub included: bool,
380}
381
382#[cfg(any(feature = "server", feature = "verify"))]
384#[derive(Clone, Debug, PartialEq, Default)]
385pub struct InternalClauses {
386 pub primary_key_in_clause: Option<WhereClause>,
388 pub primary_key_equal_clause: Option<WhereClause>,
390 pub in_clauses: Vec<WhereClause>,
397 pub range_clause: Option<WhereClause>,
404 pub equal_clauses: BTreeMap<String, WhereClause>,
406}
407
408#[cfg(any(feature = "server", feature = "verify"))]
414#[derive(Debug, Clone, Copy, Default, PartialEq, Eq)]
415pub struct ClauseFieldRoles {
416 pub primary_key: bool,
418 pub index_property: bool,
420 pub terminal: bool,
423}
424
425#[cfg(any(feature = "server", feature = "verify"))]
426impl ClauseFieldRoles {
427 pub fn unindexed(&self) -> bool {
430 !self.primary_key && !self.index_property && !self.terminal
431 }
432}
433
434#[cfg(any(feature = "server", feature = "verify"))]
441pub(crate) enum BestIndexOutcome<'a> {
442 Matched(&'a Index),
444 NoIndexMatches(Error),
447}
448
449impl InternalClauses {
450 #[cfg(any(feature = "server", feature = "verify"))]
454 pub fn skip_if_absent_bindings<'a>(
455 &'a self,
456 order_by_keys: &[&'a str],
457 ) -> Vec<SkipIfAbsentBinding<'a>> {
458 self.equal_clauses
459 .values()
460 .chain(self.in_clauses.iter())
461 .chain(self.range_clause.iter())
462 .map(SkipIfAbsentBinding::for_where_clause)
463 .chain(
464 order_by_keys
465 .iter()
466 .map(|key| SkipIfAbsentBinding::ordering(key)),
467 )
468 .collect()
469 }
470
471 #[cfg(any(feature = "server", feature = "verify"))]
477 pub fn classify_field(document_type: DocumentTypeRef, field: &str) -> ClauseFieldRoles {
478 let mut roles = ClauseFieldRoles {
479 primary_key: field == "$id",
480 ..Default::default()
481 };
482 for index in document_type.indexes().values() {
483 if index
484 .properties
485 .iter()
486 .any(|property| property.name == field)
487 {
488 roles.index_property = true;
489 }
490 if index.terminal_contains(field) {
491 roles.terminal = true;
492 }
493 if roles.index_property && roles.terminal {
494 break;
495 }
496 }
497 roles
498 }
499
500 #[cfg(any(feature = "server", feature = "verify"))]
504 pub fn classify_fields(
505 &self,
506 document_type: DocumentTypeRef,
507 ) -> BTreeMap<String, ClauseFieldRoles> {
508 let mut classified = BTreeMap::new();
509 let mut add = |field: &str| {
510 classified
511 .entry(field.to_string())
512 .or_insert_with(|| Self::classify_field(document_type, field));
513 };
514 if self.primary_key_equal_clause.is_some() || self.primary_key_in_clause.is_some() {
515 add("$id");
516 }
517 for field in self.equal_clauses.keys() {
518 add(field);
519 }
520 if let Some(range_clause) = &self.range_clause {
521 add(&range_clause.field);
522 }
523 for in_clause in &self.in_clauses {
524 add(&in_clause.field);
525 }
526 classified
527 }
528
529 #[cfg(any(feature = "server", feature = "verify"))]
530 pub fn verify(&self) -> bool {
532 if self
534 .primary_key_in_clause
535 .is_some()
536 .bitxor(self.primary_key_equal_clause.is_some())
537 {
538 !(!self.in_clauses.is_empty()
540 || self.range_clause.is_some()
541 || !self.equal_clauses.is_empty())
542 } else {
543 !(self.primary_key_in_clause.is_some() && self.primary_key_equal_clause.is_some())
544 }
545 }
546
547 #[cfg(any(feature = "server", feature = "verify"))]
548 pub fn is_for_primary_key(&self) -> bool {
550 self.primary_key_in_clause.is_some() || self.primary_key_equal_clause.is_some()
551 }
552
553 #[cfg(any(feature = "server", feature = "verify"))]
554 pub fn is_empty(&self) -> bool {
556 self.in_clauses.is_empty()
557 && self.range_clause.is_none()
558 && self.equal_clauses.is_empty()
559 && self.primary_key_in_clause.is_none()
560 && self.primary_key_equal_clause.is_none()
561 }
562
563 #[cfg(any(feature = "server", feature = "verify"))]
564 pub fn extract_from_clauses(
566 all_where_clauses: Vec<WhereClause>,
567 platform_version: &PlatformVersion,
568 ) -> Result<Self, Error> {
569 let primary_key_equal_clauses_array = all_where_clauses
570 .iter()
571 .filter_map(|where_clause| match where_clause.operator {
572 WhereOperator::Equal => match where_clause.is_identifier() {
573 true => Some(where_clause.clone()),
574 false => None,
575 },
576 _ => None,
577 })
578 .collect::<Vec<WhereClause>>();
579
580 let primary_key_in_clauses_array = all_where_clauses
581 .iter()
582 .filter_map(|where_clause| match where_clause.operator {
583 WhereOperator::In => match where_clause.is_identifier() {
584 true => Some(where_clause.clone()),
585 false => None,
586 },
587 _ => None,
588 })
589 .collect::<Vec<WhereClause>>();
590
591 let (equal_clauses, range_clause, in_clauses) =
592 WhereClause::group_clauses(&all_where_clauses, platform_version)?;
593
594 let primary_key_equal_clause = match primary_key_equal_clauses_array.len() {
595 0 => Ok(None),
596 1 => Ok(Some(
597 primary_key_equal_clauses_array
598 .first()
599 .expect("there must be a value")
600 .clone(),
601 )),
602 _ => Err(Error::Query(
603 QuerySyntaxError::DuplicateNonGroupableClauseSameField(
604 "There should only be one equal clause for the primary key",
605 ),
606 )),
607 }?;
608
609 let primary_key_in_clause = match primary_key_in_clauses_array.len() {
610 0 => Ok(None),
611 1 => Ok(Some(
612 primary_key_in_clauses_array
613 .first()
614 .expect("there must be a value")
615 .clone(),
616 )),
617 _ => Err(Error::Query(
618 QuerySyntaxError::DuplicateNonGroupableClauseSameField(
619 "There should only be one in clause for the primary key",
620 ),
621 )),
622 }?;
623
624 let internal_clauses = InternalClauses {
625 primary_key_equal_clause,
626 primary_key_in_clause,
627 in_clauses,
628 range_clause,
629 equal_clauses,
630 };
631
632 match internal_clauses.verify() {
633 true => Ok(internal_clauses),
634 false => Err(Error::Query(
635 QuerySyntaxError::InvalidWhereClauseComponents("Query has invalid where clauses"),
636 )),
637 }
638 }
639
640 #[cfg(any(feature = "server", feature = "verify"))]
642 pub fn validate_against_schema(
643 &self,
644 document_type: DocumentTypeRef,
645 ) -> QuerySyntaxSimpleValidationResult {
646 if !self.verify() {
648 return QuerySyntaxSimpleValidationResult::new_with_error(
649 QuerySyntaxError::InvalidWhereClauseComponents(
650 "invalid composition of where clauses",
651 ),
652 );
653 }
654
655 for in_clause in &self.in_clauses {
657 if in_clause.field == "$id" {
659 return QuerySyntaxSimpleValidationResult::new_with_error(
660 QuerySyntaxError::InvalidWhereClauseComponents(
661 "use primary_key_* clauses for $id",
662 ),
663 );
664 }
665 let result = in_clause.validate_against_schema(document_type);
666 if !result.is_valid() {
667 return result;
668 }
669 }
670
671 if let Some(range_clause) = &self.range_clause {
673 if range_clause.field == "$id" {
675 return QuerySyntaxSimpleValidationResult::new_with_error(
676 QuerySyntaxError::InvalidWhereClauseComponents(
677 "use primary_key_* clauses for $id",
678 ),
679 );
680 }
681 let result = range_clause.validate_against_schema(document_type);
682 if !result.is_valid() {
683 return result;
684 }
685 }
686
687 for (field, eq_clause) in &self.equal_clauses {
689 if field.as_str() == "$id" {
691 return QuerySyntaxSimpleValidationResult::new_with_error(
692 QuerySyntaxError::InvalidWhereClauseComponents(
693 "use primary_key_* clauses for $id",
694 ),
695 );
696 }
697 let result = eq_clause.validate_against_schema(document_type);
698 if !result.is_valid() {
699 return result;
700 }
701 }
702
703 if let Some(pk_eq) = &self.primary_key_equal_clause {
705 if pk_eq.operator != WhereOperator::Equal
706 || !matches!(pk_eq.value, Value::Identifier(_))
707 {
708 return QuerySyntaxSimpleValidationResult::new_with_error(
709 QuerySyntaxError::InvalidWhereClauseComponents(
710 "primary key equality must compare an identifier",
711 ),
712 );
713 }
714 }
715 if let Some(pk_in) = &self.primary_key_in_clause {
716 if pk_in.operator != WhereOperator::In {
717 return QuerySyntaxSimpleValidationResult::new_with_error(
718 QuerySyntaxError::InvalidWhereClauseComponents(
719 "primary key IN must use IN operator",
720 ),
721 );
722 }
723 let result = pk_in.in_values();
725 if !result.is_valid() {
726 return QuerySyntaxSimpleValidationResult::new_with_errors(result.errors);
727 }
728 if let Value::Array(arr) = &pk_in.value {
729 if !arr.iter().all(|v| matches!(v, Value::Identifier(_))) {
730 return QuerySyntaxSimpleValidationResult::new_with_error(
731 QuerySyntaxError::InvalidWhereClauseComponents(
732 "primary key IN must contain identifiers",
733 ),
734 );
735 }
736 } else {
737 return QuerySyntaxSimpleValidationResult::new_with_error(
738 QuerySyntaxError::InvalidWhereClauseComponents(
739 "primary key IN must contain an array of identifiers",
740 ),
741 );
742 }
743 }
744
745 QuerySyntaxSimpleValidationResult::default()
746 }
747}
748
749impl From<InternalClauses> for Vec<WhereClause> {
750 fn from(clauses: InternalClauses) -> Self {
751 let mut result: Self = clauses.equal_clauses.into_values().collect();
752
753 result.extend(clauses.in_clauses);
754 if let Some(clause) = clauses.primary_key_equal_clause {
755 result.push(clause);
756 };
757 if let Some(clause) = clauses.primary_key_in_clause {
758 result.push(clause);
759 };
760 if let Some(clause) = clauses.range_clause {
761 result.push(clause);
762 };
763
764 result
765 }
766}
767
768#[cfg(any(feature = "server", feature = "verify"))]
778#[derive(Copy, Clone, Debug, PartialEq, Eq)]
779#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
780#[cfg_attr(feature = "serde", serde(rename_all = "lowercase"))]
781pub enum TimeRangeSelector {
782 Newest,
785 Oldest,
788 ByStart {
796 start_ms: u64,
798 },
799}
800
801#[cfg(any(feature = "server", feature = "verify"))]
802impl TimeRangeSelector {
803 pub fn as_str(&self) -> &'static str {
813 match self {
814 TimeRangeSelector::Newest => "newest",
815 TimeRangeSelector::Oldest => "oldest",
816 TimeRangeSelector::ByStart { .. } => "byStart",
817 }
818 }
819
820 pub fn from_string(value: &str) -> Option<Self> {
824 match value {
825 "newest" => Some(TimeRangeSelector::Newest),
826 "oldest" => Some(TimeRangeSelector::Oldest),
827 _ => None,
828 }
829 }
830}
831
832#[cfg(any(feature = "server", feature = "verify"))]
841#[derive(Debug, Clone, Copy, PartialEq, Eq)]
842#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
843pub struct TimeRangeGridSpec {
844 pub range_seconds: u64,
846 pub step_seconds: u64,
848 pub phase_seconds: u64,
850}
851
852#[cfg(any(feature = "server", feature = "verify"))]
853impl TimeRangeGridSpec {
854 pub fn matches(&self, transform: &TimeRangeTransform) -> bool {
856 self.range_seconds == transform.range_seconds
857 && self.step_seconds == transform.step_seconds
858 && self.phase_seconds == transform.phase_seconds
859 }
860}
861
862#[cfg(any(feature = "server", feature = "verify"))]
875#[derive(Debug, Clone, PartialEq)]
876pub struct ResolvedTimeRange {
877 pub transform: IndexBucketing,
881}
882
883#[cfg(any(feature = "server", feature = "verify"))]
884impl ResolvedTimeRange {
885 pub fn field(&self) -> &str {
888 self.transform.source()
889 }
890
891 pub fn kind(&self) -> &'static str {
894 match self.transform {
895 IndexBucketing::Time(_) => "time-range",
896 IndexBucketing::Integer(_) => "integer-range",
897 }
898 }
899
900 pub fn operator(&self) -> &'static str {
902 match self.transform {
903 IndexBucketing::Time(_) => "IN_TIME_RANGE",
904 IndexBucketing::Integer(_) => "IN_INTEGER_RANGE",
905 }
906 }
907}
908
909#[cfg(any(feature = "server", feature = "verify"))]
911struct SelectionGridKind {
912 kind: &'static str,
914 operator: &'static str,
916 units: &'static str,
918}
919
920#[cfg(any(feature = "server", feature = "verify"))]
927fn select_selection_grid<'a, T>(
928 document_type: &'a DocumentTypeRef<'_>,
929 field: &str,
930 grid_of: impl Fn(&'a Index) -> Option<&'a T>,
931 spec: Option<(impl Fn(&T) -> bool, String)>,
932 names: SelectionGridKind,
933) -> Result<&'a T, Error>
934where
935 T: BucketSource + PartialEq,
936{
937 let mut grids: Vec<&'a T> = Vec::new();
938 for index in document_type.indexes().values() {
939 if let Some(transform) = grid_of(index).filter(|transform| transform.source() == field) {
940 if !grids.contains(&transform) {
941 grids.push(transform);
942 }
943 }
944 }
945 let SelectionGridKind {
946 kind,
947 operator,
948 units,
949 } = names;
950 let Some(first) = grids.first().copied() else {
951 return Err(Error::Query(
952 QuerySyntaxError::WhereClauseOnNonIndexedProperty(format!(
953 "no {kind} index is defined on field \"{field}\""
954 )),
955 ));
956 };
957 match spec {
958 Some((matches, described)) => grids
959 .into_iter()
960 .find(|transform| matches(transform))
961 .ok_or(Error::Query(QuerySyntaxError::Unsupported(format!(
962 "no {kind} index on \"{field}\" declares the grid {described}"
963 )))),
964 None if grids.len() > 1 => Err(Error::Query(QuerySyntaxError::Unsupported(format!(
965 "field \"{field}\" is bucketed by {} different {kind} grids; the {operator} \
966 selection must name one in its `grid` (range/step/phase{units} as the contract \
967 declares them)",
968 grids.len()
969 )))),
970 None => Ok(first),
971 }
972}
973
974#[cfg(any(feature = "server", feature = "verify"))]
976trait BucketSource {
977 fn source(&self) -> &str;
978}
979
980#[cfg(any(feature = "server", feature = "verify"))]
981impl BucketSource for TimeRangeTransform {
982 fn source(&self) -> &str {
983 &self.source
984 }
985}
986
987#[cfg(any(feature = "server", feature = "verify"))]
988impl BucketSource for IntegerRangeTransform {
989 fn source(&self) -> &str {
990 &self.source
991 }
992}
993
994#[cfg(any(feature = "server", feature = "verify"))]
1022pub fn resolve_time_range_bucket_clause(
1023 field: &str,
1024 selector: TimeRangeSelector,
1025 grid: Option<TimeRangeGridSpec>,
1026 document_type: DocumentTypeRef,
1027 block_time_ms: u64,
1028) -> Result<(WhereClause, ResolvedTimeRange), Error> {
1029 let transform = select_selection_grid(
1030 &document_type,
1031 field,
1032 |index| index.time_range.as_ref(),
1033 grid.map(|spec| {
1034 (
1035 move |transform: &TimeRangeTransform| spec.matches(transform),
1036 format!(
1037 "range={}s step={}s phase={}s",
1038 spec.range_seconds, spec.step_seconds, spec.phase_seconds
1039 ),
1040 )
1041 }),
1042 SelectionGridKind {
1043 kind: "time-range",
1044 operator: "IN_TIME_RANGE",
1045 units: ", in seconds,",
1046 },
1047 )?;
1048
1049 let bucket_start = match selector {
1050 TimeRangeSelector::Newest => transform.newest_active_start(block_time_ms),
1051 TimeRangeSelector::Oldest => transform.oldest_active_start(block_time_ms),
1052 TimeRangeSelector::ByStart { start_ms } => {
1058 if !transform.is_bucket_start(start_ms) {
1059 return Err(Error::Query(QuerySyntaxError::Unsupported(format!(
1060 "byStart {} on \"{}\" is not a window start of the grid range={}s \
1061 step={}s phase={}s: starts are phase + k*step on the millisecond \
1062 timeline, and an off-grid start is rejected rather than snapped",
1063 start_ms,
1064 field,
1065 transform.range_seconds,
1066 transform.step_seconds,
1067 transform.phase_seconds
1068 ))));
1069 }
1070 if transform.bucket_expired(start_ms, block_time_ms) {
1080 return Err(Error::Query(QuerySyntaxError::Unsupported(format!(
1081 "byStart {} on \"{}\" is past the ttl horizon ({}s): expired windows \
1082 drain lazily and may be mid-removal, so they are not queryable — \
1083 entries under this index live at most `ttl` past their window's start",
1084 start_ms,
1085 field,
1086 transform.ttl_seconds.unwrap_or_default()
1087 ))));
1088 }
1089 Some(start_ms)
1090 }
1091 }
1092 .ok_or(Error::Query(QuerySyntaxError::Unsupported(format!(
1093 "no time range on \"{}\" is active yet: the block time predates the grid's phase \
1094 anchor (only possible within the first step after the epoch)",
1095 field
1096 ))))?;
1097
1098 Ok((
1099 WhereClause {
1100 field: field.to_string(),
1101 operator: WhereOperator::Equal,
1102 value: Value::U64(bucket_start),
1103 },
1104 ResolvedTimeRange {
1105 transform: transform.clone().into(),
1106 },
1107 ))
1108}
1109
1110#[cfg(any(feature = "server", feature = "verify"))]
1116#[derive(Debug, Clone, Copy, PartialEq, Eq)]
1117#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
1118pub struct IntegerRangeGridSpec {
1119 pub range: u64,
1121 pub step: u64,
1123 pub phase: u64,
1125}
1126
1127#[cfg(any(feature = "server", feature = "verify"))]
1128impl IntegerRangeGridSpec {
1129 pub fn matches(&self, transform: &IntegerRangeTransform) -> bool {
1131 self.range == transform.range
1132 && self.step == transform.step
1133 && self.phase == transform.phase
1134 }
1135}
1136
1137#[cfg(any(feature = "server", feature = "verify"))]
1158pub fn resolve_integer_range_bucket_clause(
1159 field: &str,
1160 start: &Value,
1161 grid: Option<IntegerRangeGridSpec>,
1162 document_type: DocumentTypeRef,
1163) -> Result<(WhereClause, ResolvedTimeRange), Error> {
1164 let transform = select_selection_grid(
1165 &document_type,
1166 field,
1167 |index| index.integer_range.as_ref(),
1168 grid.map(|spec| {
1169 (
1170 move |transform: &IntegerRangeTransform| spec.matches(transform),
1171 format!(
1172 "range={} step={} phase={}",
1173 spec.range, spec.step, spec.phase
1174 ),
1175 )
1176 }),
1177 SelectionGridKind {
1178 kind: "integer-range",
1179 operator: "IN_INTEGER_RANGE",
1180 units: "",
1181 },
1182 )?;
1183
1184 let start = start.as_integer::<i128>().ok_or(Error::Query(
1185 QuerySyntaxError::InvalidWhereClauseComponents(
1186 "an IN_INTEGER_RANGE selection's start must be an integer",
1187 ),
1188 ))?;
1189 if !transform.is_window_start(start) {
1190 return Err(Error::Query(QuerySyntaxError::Unsupported(format!(
1191 "{} on \"{}\" is not a window start of the grid range={} step={} phase={}: starts \
1192 are phase + k*step within the property's integer type (or the type's minimum, for \
1193 the clamped bottom window), and an off-grid start is rejected rather than snapped",
1194 start, field, transform.range, transform.step, transform.phase
1195 ))));
1196 }
1197 let value = transform.key_type.value_of(start).ok_or(Error::Drive(
1200 DriveError::CorruptedCodeExecution("a window start is held by its key type"),
1201 ))?;
1202
1203 Ok((
1204 WhereClause {
1205 field: field.to_string(),
1206 operator: WhereOperator::Equal,
1207 value,
1208 },
1209 ResolvedTimeRange {
1210 transform: transform.clone().into(),
1211 },
1212 ))
1213}
1214
1215#[cfg(any(feature = "server", feature = "verify"))]
1244pub fn index_admissible_for_resolved_time_range(
1245 index: &Index,
1246 resolved_time_ranges: &[ResolvedTimeRange],
1247) -> bool {
1248 match resolved_time_ranges {
1249 [] => !index.is_bucketed(),
1250 [resolved] => index.is_bucketed_by(&resolved.transform),
1257 _ => false,
1258 }
1259}
1260
1261#[cfg(any(feature = "server", feature = "verify"))]
1264#[derive(Clone, Copy, Debug, PartialEq, Eq)]
1265pub struct SkipIfAbsentBinding<'a> {
1266 pub field: &'a str,
1268 pub excludes_missing: bool,
1273 pub constrains: bool,
1278}
1279
1280#[cfg(any(feature = "server", feature = "verify"))]
1281impl<'a> SkipIfAbsentBinding<'a> {
1282 pub fn ordering(field: &'a str) -> Self {
1285 Self {
1286 field,
1287 excludes_missing: false,
1288 constrains: false,
1289 }
1290 }
1291
1292 pub fn for_where_clause(clause: &'a WhereClause) -> Self {
1298 let lower_bound_excludes_missing = |value: &Value| {
1299 value
1300 .as_array()
1301 .and_then(|bounds| bounds.first())
1302 .is_some_and(|lower| !may_encode_as_missing(lower))
1303 };
1304 let excludes_missing = match clause.operator {
1305 WhereOperator::Equal | WhereOperator::GreaterThanOrEquals => {
1306 !may_encode_as_missing(&clause.value)
1307 }
1308 WhereOperator::In => clause
1311 .in_values()
1312 .data
1313 .is_some_and(|values| values.iter().all(|value| !may_encode_as_missing(value))),
1314 WhereOperator::GreaterThan
1315 | WhereOperator::BetweenExcludeBounds
1316 | WhereOperator::BetweenExcludeLeft => true,
1317 WhereOperator::Contains | WhereOperator::ContainsAny => true,
1321 WhereOperator::Between | WhereOperator::BetweenExcludeRight => {
1322 lower_bound_excludes_missing(&clause.value)
1323 }
1324 WhereOperator::StartsWith => clause
1325 .value
1326 .as_text()
1327 .is_some_and(|prefix| !prefix.is_empty()),
1328 WhereOperator::LessThan | WhereOperator::LessThanOrEquals => false,
1329 };
1330 Self {
1331 field: clause.field.as_str(),
1332 excludes_missing,
1333 constrains: true,
1334 }
1335 }
1336
1337 pub fn for_where_clauses(where_clauses: &'a [WhereClause]) -> Vec<Self> {
1339 where_clauses.iter().map(Self::for_where_clause).collect()
1340 }
1341}
1342
1343#[cfg(any(feature = "server", feature = "verify"))]
1349fn may_encode_as_missing(value: &Value) -> bool {
1350 match value {
1351 Value::Null => true,
1352 Value::Bytes(bytes) => bytes.is_empty(),
1353 Value::Array(values) => values.is_empty(),
1354 Value::Text(text) => text.is_empty(),
1355 _ => false,
1356 }
1357}
1358
1359#[cfg(any(feature = "server", feature = "verify"))]
1387pub fn index_admissible_for_skip_if_absent(
1388 index: &Index,
1389 bindings: &[SkipIfAbsentBinding<'_>],
1390) -> bool {
1391 let array_bound = |array: &str| {
1401 bindings
1402 .iter()
1403 .any(|binding| binding.field == array && binding.constrains)
1404 };
1405 if !index.skip_if_absent {
1406 return index.indexed_array.as_deref().is_none_or(array_bound);
1407 }
1408 let index_only = index.is_index_only();
1412 index.skip_if_absent_properties.iter().all(|skip_property| {
1413 if index.indexed_array.as_ref() == Some(skip_property) {
1414 return array_bound(skip_property);
1415 }
1416 bindings.iter().any(|binding| {
1417 binding.field == skip_property.as_str() && (index_only || binding.excludes_missing)
1418 })
1419 })
1420}
1421
1422#[cfg(any(feature = "server", feature = "verify"))]
1430pub fn index_admissible_for_query(
1431 index: &Index,
1432 resolved_time_ranges: &[ResolvedTimeRange],
1433 skip_bindings: &[SkipIfAbsentBinding<'_>],
1434) -> bool {
1435 index_admissible_for_resolved_time_range(index, resolved_time_ranges)
1436 && index_admissible_for_skip_if_absent(index, skip_bindings)
1437}
1438
1439#[cfg(any(feature = "server", feature = "verify"))]
1448pub fn document_index_admissible_for_query(
1449 index: &Index,
1450 resolved_time_ranges: &[ResolvedTimeRange],
1451 skip_bindings: &[SkipIfAbsentBinding<'_>],
1452) -> bool {
1453 !index.is_summable_off_count_index()
1454 && index_admissible_for_query(index, resolved_time_ranges, skip_bindings)
1455}
1456
1457#[cfg(any(feature = "server", feature = "verify"))]
1465pub fn pins_reach_chain(index: &Index, pin_depth: usize, chain_position: Option<usize>) -> bool {
1466 pin_depth >= 1
1467 && pin_depth < index.properties.len()
1468 && chain_position.is_some_and(|min_at| min_at < pin_depth)
1469}
1470
1471#[cfg(any(feature = "server", feature = "verify"))]
1495pub(crate) fn index_keeps_empty_groups(document_type: DocumentTypeRef, index: &Index) -> bool {
1496 document_type.indexes().values().any(|other| {
1497 (other.preallocated || other.outlives_delete)
1498 && other.shares_leading_levels(index, index.properties.len())
1499 })
1500}
1501
1502#[cfg(feature = "server")]
1506pub(crate) fn is_absent_path(error: &Error) -> bool {
1507 matches!(
1508 error,
1509 Error::GroveDB(e) if matches!(
1510 e.as_ref(),
1511 grovedb::Error::PathKeyNotFound(_)
1512 | grovedb::Error::PathNotFound(_)
1513 | grovedb::Error::PathParentLayerNotFound(_)
1514 )
1515 )
1516}
1517
1518#[cfg(feature = "server")]
1523pub fn uncovered_required_property_refusal() -> QuerySyntaxError {
1524 QuerySyntaxError::Unsupported(
1525 "this indexOnly query's index does not cover every required property, so the \
1526 documents it synthesizes cannot be serialized into a non-proof response; query \
1527 through an index covering all properties, or use a proved query"
1528 .to_string(),
1529 )
1530}
1531
1532#[cfg(feature = "server")]
1539pub fn index_only_serialization_refusal(error: &ProtocolError) -> Option<QuerySyntaxError> {
1540 matches!(
1541 error,
1542 ProtocolError::DataContractError(
1543 dpp::data_contract::errors::DataContractError::MissingRequiredKey(_)
1544 )
1545 )
1546 .then(uncovered_required_property_refusal)
1547}
1548
1549#[cfg(feature = "server")]
1571pub(crate) fn aggregate_or_zero_when_absent<T: Default>(
1572 drive: &Drive,
1573 path: &[Vec<u8>],
1574 value: Result<T, grovedb::Error>,
1575 range_total_verifier: FeatureVersion,
1576 transaction: TransactionArg,
1577 platform_version: &PlatformVersion,
1578) -> Result<T, Error> {
1579 match (value, range_total_verifier) {
1580 (value, 0) => value.map_err(|e| Error::GroveDB(Box::new(e))),
1581 (Err(error @ grovedb::Error::InvalidParentLayerPath(_)), 1) => {
1582 for depth in 0..path.len() {
1583 let found = drive
1584 .grove
1585 .get_raw_optional(
1586 path[..depth].into(),
1587 &path[depth],
1588 transaction,
1589 &platform_version.drive.grove_version,
1590 )
1591 .unwrap()
1592 .map_err(|e| Error::GroveDB(Box::new(e)))?;
1593 if found.is_none() {
1594 return Ok(T::default());
1595 }
1596 }
1597 Err(Error::GroveDB(Box::new(error)))
1598 }
1599 (value, 1) => value.map_err(|e| Error::GroveDB(Box::new(e))),
1600 (_, version) => Err(Error::Drive(DriveError::UnknownVersionMismatch {
1601 method: "aggregate_or_zero_when_absent".to_string(),
1602 known_versions: vec![0, 1],
1603 received: version,
1604 })),
1605 }
1606}
1607
1608#[cfg(any(feature = "server", feature = "verify"))]
1621pub(crate) fn prefix_to_last_path_query(
1622 base_path: Vec<Vec<u8>>,
1623 in_outer_keys: Option<Vec<Vec<u8>>>,
1624 subquery_path_extension: Vec<Vec<u8>>,
1625 terminal_level_key: Vec<u8>,
1626) -> PathQuery {
1627 match in_outer_keys {
1628 None => {
1629 let mut query = Query::new();
1630 query.insert_key(terminal_level_key);
1631 PathQuery::new(base_path, SizedQuery::new(query, None, None))
1632 }
1633 Some(keys) => {
1634 let mut outer_query = Query::new();
1635 for key in keys {
1636 outer_query.insert_key(key);
1637 }
1638 let mut subquery = Query::new();
1639 subquery.insert_key(terminal_level_key);
1640 if !subquery_path_extension.is_empty() {
1641 outer_query.set_subquery_path(subquery_path_extension);
1642 }
1643 outer_query.set_subquery(subquery);
1644 PathQuery::new(base_path, SizedQuery::new(outer_query, None, None))
1645 }
1646 }
1647}
1648
1649#[cfg(any(feature = "server", feature = "verify"))]
1670pub fn refuse_a_range_total_through_a_ranked_index(
1671 document_type: DocumentTypeRef,
1672 index: &Index,
1673) -> Result<(), Error> {
1674 let mut level = document_type.index_structure();
1675 for (position, property) in index.properties.iter().enumerate() {
1676 let Some(sub_level) = level
1677 .sub_levels()
1678 .get(&index.level_key(position, &property.name))
1679 else {
1680 return Ok(());
1681 };
1682 if !property_name_tree_type_and_ranked_axes_for_level(sub_level)?
1683 .1
1684 .is_empty()
1685 {
1686 let last = index
1687 .properties
1688 .last()
1689 .map(|property| property.name.as_str())
1690 .unwrap_or_default();
1691 return Err(Error::Query(QuerySyntaxError::Unsupported(format!(
1692 "a range total over the index `{}` is not available: its path passes through a \
1693 ranked level, and a range total is read only through unranked trees; group by \
1694 `{last}` to read each value in the range",
1695 index.name
1696 ))));
1697 }
1698 level = sub_level;
1699 }
1700 Ok(())
1701}
1702
1703#[cfg(any(feature = "server", feature = "verify"))]
1718pub fn validate_resolved_time_range_clause_shapes(
1719 where_clauses: &[WhereClause],
1720 resolved_time_ranges: &[ResolvedTimeRange],
1721) -> Result<(), Error> {
1722 for field in resolved_time_ranges.iter().map(|resolved| resolved.field()) {
1723 let mut equalities = 0usize;
1724 for clause in where_clauses.iter().filter(|c| c.field == field) {
1725 if clause.operator == WhereOperator::Equal {
1726 equalities += 1;
1727 } else {
1728 return Err(Error::Query(
1729 QuerySyntaxError::InvalidWhereClauseComponents(
1730 "a field resolved from a window selection (IN_TIME_RANGE or \
1731 IN_INTEGER_RANGE) may only carry the single equality its resolution \
1732 produced, not a range or In clause",
1733 ),
1734 ));
1735 }
1736 }
1737 if equalities != 1 {
1738 return Err(Error::Query(
1739 QuerySyntaxError::InvalidWhereClauseComponents(
1740 "a field resolved from a window selection (IN_TIME_RANGE or \
1741 IN_INTEGER_RANGE) must carry exactly one equality clause — the one its \
1742 resolution produced",
1743 ),
1744 ));
1745 }
1746 }
1747 Ok(())
1748}
1749
1750#[cfg(any(feature = "server", feature = "verify"))]
1751#[derive(Debug, PartialEq, Clone)]
1753pub struct DriveDocumentQuery<'a> {
1754 pub contract: &'a DataContract,
1756 pub document_type: DocumentTypeRef<'a>,
1758 pub internal_clauses: InternalClauses,
1760 pub offset: Option<u16>,
1762 pub limit: Option<u16>,
1764 pub order_by: IndexMap<String, OrderClause>,
1766 pub start_at: Option<[u8; 32]>,
1768 pub start_at_included: bool,
1770 pub block_time_ms: Option<u64>,
1772 pub resolved_time_ranges: Vec<ResolvedTimeRange>,
1788 pub sub_queries: Vec<DriveSubQuery<'a>>,
1807}
1808
1809impl<'a> DriveDocumentQuery<'a> {
1810 #[cfg(any(feature = "server", feature = "verify"))]
1812 pub fn new_primary_key_single_item_query(
1813 contract: &'a DataContract,
1814 document_type: DocumentTypeRef<'a>,
1815 id: Identifier,
1816 ) -> Self {
1817 DriveDocumentQuery {
1818 contract,
1819 document_type,
1820 internal_clauses: InternalClauses {
1821 primary_key_in_clause: None,
1822 primary_key_equal_clause: Some(WhereClause {
1823 field: document::property_names::ID.to_string(),
1824 operator: WhereOperator::Equal,
1825 value: Value::Identifier(id.to_buffer()),
1826 }),
1827 in_clauses: Vec::new(),
1828 range_clause: None,
1829 equal_clauses: Default::default(),
1830 },
1831 offset: None,
1832 limit: None,
1833 order_by: Default::default(),
1834 start_at: None,
1835 start_at_included: false,
1836 block_time_ms: None,
1837 resolved_time_ranges: vec![],
1838 sub_queries: vec![],
1839 }
1840 }
1841
1842 #[cfg(feature = "server")]
1843 pub fn any_item_query(contract: &'a DataContract, document_type: DocumentTypeRef<'a>) -> Self {
1845 DriveDocumentQuery {
1846 contract,
1847 document_type,
1848 internal_clauses: Default::default(),
1849 offset: None,
1850 limit: Some(1),
1851 order_by: Default::default(),
1852 start_at: None,
1853 start_at_included: true,
1854 block_time_ms: None,
1855 resolved_time_ranges: vec![],
1856 sub_queries: vec![],
1857 }
1858 }
1859
1860 #[cfg(feature = "server")]
1861 pub fn all_items_query(
1863 contract: &'a DataContract,
1864 document_type: DocumentTypeRef<'a>,
1865 limit: Option<u16>,
1866 ) -> Self {
1867 DriveDocumentQuery {
1868 contract,
1869 document_type,
1870 internal_clauses: Default::default(),
1871 offset: None,
1872 limit,
1873 order_by: Default::default(),
1874 start_at: None,
1875 start_at_included: true,
1876 block_time_ms: None,
1877 resolved_time_ranges: vec![],
1878 sub_queries: vec![],
1879 }
1880 }
1881
1882 #[cfg(any(feature = "server", feature = "verify"))]
1883 pub fn with_sub_queries(mut self, sub_queries: Vec<DriveSubQuery<'a>>) -> Self {
1888 self.sub_queries = sub_queries;
1889 self
1890 }
1891
1892 #[cfg(any(feature = "server", feature = "verify"))]
1893 pub fn with_by_id_join(
1906 mut self,
1907 source_property: impl Into<String>,
1908 document_type: DocumentTypeRef<'a>,
1909 ) -> Self {
1910 self.sub_queries.push(DriveSubQuery {
1911 contract: self.contract,
1912 document_type,
1913 kind: SubQueryKind::Documents,
1914 where_clauses: vec![],
1915 order_by: vec![],
1916 limit: None,
1917 binding: Some(SubQueryBinding {
1918 source: BindingSource::Page,
1919 source_property: source_property.into(),
1920 field: document::property_names::ID.to_string(),
1921 }),
1922 });
1923 self
1924 }
1925
1926 #[cfg(any(feature = "server", feature = "verify"))]
1927 pub(crate) fn ensure_no_sub_queries(&self, surface: &str) -> Result<(), Error> {
1932 if self.sub_queries.is_empty() {
1933 return Ok(());
1934 }
1935 Err(Error::Query(QuerySyntaxError::Unsupported(format!(
1936 "this query carries {} sub-queries, which {} would silently ignore; execute and \
1937 verify it on the composite surface (query_composite_documents / \
1938 verify_composite_documents_proof) or, for a single by-id join, the chained one \
1939 (query_chained_documents / verify_chained_documents_proof)",
1940 self.sub_queries.len(),
1941 surface,
1942 ))))
1943 }
1944
1945 #[cfg(any(feature = "server", feature = "verify"))]
1946 pub fn is_for_primary_key(&self) -> bool {
1948 self.internal_clauses.is_for_primary_key()
1949 || (self.internal_clauses.is_empty()
1950 && (self.order_by.is_empty()
1951 || (self.order_by.len() == 1
1952 && self
1953 .order_by
1954 .keys()
1955 .collect::<Vec<&String>>()
1956 .first()
1957 .unwrap()
1958 .as_str()
1959 == "$id")))
1960 }
1961
1962 #[cfg(feature = "cbor_query")]
1963 pub fn from_cbor(
1965 query_cbor: &[u8],
1966 contract: &'a DataContract,
1967 document_type: DocumentTypeRef<'a>,
1968 config: &DriveConfig,
1969 platform_version: &PlatformVersion,
1970 ) -> Result<Self, Error> {
1971 let query_document_value: Value = ciborium::de::from_reader(query_cbor).map_err(|_| {
1972 Error::Query(QuerySyntaxError::DeserializationError(
1973 "unable to decode query from cbor".to_string(),
1974 ))
1975 })?;
1976 Self::from_value(
1977 query_document_value,
1978 contract,
1979 document_type,
1980 config,
1981 platform_version,
1982 )
1983 }
1984
1985 #[cfg(any(feature = "server", feature = "verify"))]
1986 pub fn from_value(
1988 query_value: Value,
1989 contract: &'a DataContract,
1990 document_type: DocumentTypeRef<'a>,
1991 config: &DriveConfig,
1992 platform_version: &PlatformVersion,
1993 ) -> Result<Self, Error> {
1994 let query_document: BTreeMap<String, Value> = query_value.into_btree_string_map()?;
1995 Self::from_btree_map_value(
1996 query_document,
1997 contract,
1998 document_type,
1999 config,
2000 platform_version,
2001 )
2002 }
2003
2004 #[cfg(any(feature = "server", feature = "verify"))]
2005 pub fn from_btree_map_value(
2007 mut query_document: BTreeMap<String, Value>,
2008 contract: &'a DataContract,
2009 document_type: DocumentTypeRef<'a>,
2010 config: &DriveConfig,
2011 platform_version: &PlatformVersion,
2012 ) -> Result<Self, Error> {
2013 if let Some(contract_id) = query_document
2014 .remove_optional_identifier("contract_id")
2015 .map_err(|e| Error::Protocol(Box::new(ProtocolError::ValueError(e))))?
2016 {
2017 if contract.id() != contract_id {
2018 return Err(ProtocolError::IdentifierError(format!(
2019 "data contract id mismatch, expected: {}, got: {}",
2020 contract.id(),
2021 contract_id
2022 ))
2023 .into());
2024 };
2025 }
2026
2027 if let Some(document_type_name) = query_document
2028 .remove_optional_string("document_type_name")
2029 .map_err(|e| Error::Protocol(Box::new(ProtocolError::ValueError(e))))?
2030 {
2031 if document_type.name() != &document_type_name {
2032 return Err(ProtocolError::IdentifierError(format!(
2033 "document type name mismatch, expected: {}, got: {}",
2034 document_type.name(),
2035 document_type_name
2036 ))
2037 .into());
2038 }
2039 }
2040
2041 let maybe_limit: Option<u16> = query_document
2042 .remove_optional_integer("limit")
2043 .map_err(|e| Error::Protocol(Box::new(ProtocolError::ValueError(e))))?;
2044
2045 let limit = maybe_limit
2046 .map_or(Some(config.default_query_limit), |limit_value| {
2047 if limit_value == 0 || limit_value > config.default_query_limit {
2048 None
2049 } else {
2050 Some(limit_value)
2051 }
2052 })
2053 .ok_or(Error::Query(QuerySyntaxError::InvalidLimit(format!(
2054 "limit greater than max limit {}",
2055 config.max_query_limit
2056 ))))?;
2057
2058 let offset: Option<u16> = query_document
2059 .remove_optional_integer("offset")
2060 .map_err(|e| Error::Protocol(Box::new(ProtocolError::ValueError(e))))?;
2061
2062 let block_time_ms: Option<u64> = query_document
2063 .remove_optional_integer("blockTime")
2064 .map_err(|e| Error::Protocol(Box::new(ProtocolError::ValueError(e))))?;
2065
2066 let all_where_clauses: Vec<WhereClause> =
2067 query_document
2068 .remove("where")
2069 .map_or(Ok(vec![]), |id_cbor| {
2070 if let Value::Array(clauses) = id_cbor {
2071 clauses
2072 .iter()
2073 .map(|where_clause| {
2074 if let Value::Array(clauses_components) = where_clause {
2075 WhereClause::from_components(clauses_components)
2076 } else {
2077 Err(Error::Query(QuerySyntaxError::InvalidFormatWhereClause(
2078 "where clause must be an array".to_string(),
2079 )))
2080 }
2081 })
2082 .collect::<Result<Vec<WhereClause>, Error>>()
2083 } else {
2084 Err(Error::Query(QuerySyntaxError::InvalidFormatWhereClause(
2085 "where clause must be an array".to_string(),
2086 )))
2087 }
2088 })?;
2089
2090 let internal_clauses =
2091 InternalClauses::extract_from_clauses(all_where_clauses, platform_version)?;
2092
2093 let start_at_option = query_document.remove("startAt");
2094 let start_after_option = query_document.remove("startAfter");
2095 if start_after_option.is_some() && start_at_option.is_some() {
2096 return Err(Error::Query(QuerySyntaxError::DuplicateStartConditions(
2097 "only one of startAt or startAfter should be provided",
2098 )));
2099 }
2100
2101 let mut start_at_included = true;
2102
2103 let mut start_option: Option<Value> = None;
2104
2105 if start_after_option.is_some() {
2106 start_option = start_after_option;
2107 start_at_included = false;
2108 } else if start_at_option.is_some() {
2109 start_option = start_at_option;
2110 start_at_included = true;
2111 }
2112
2113 let start_at: Option<[u8; 32]> = start_option
2114 .map(|v| {
2115 v.into_identifier()
2116 .map_err(|e| Error::Protocol(Box::new(ProtocolError::ValueError(e))))
2117 .map(|identifier| identifier.into_buffer())
2118 })
2119 .transpose()?;
2120
2121 let order_by: IndexMap<String, OrderClause> =
2122 query_document
2123 .remove("orderBy")
2124 .map_or(Ok(IndexMap::new()), |id_cbor| {
2125 if let Value::Array(clauses) = id_cbor {
2126 clauses
2127 .into_iter()
2128 .filter_map(|order_clause| {
2129 if let Value::Array(clauses_components) = order_clause {
2130 let order_clause =
2131 OrderClause::from_components(&clauses_components)
2132 .map_err(Error::from);
2133 match order_clause {
2134 Ok(order_clause) => {
2135 Some(Ok((order_clause.field.clone(), order_clause)))
2136 }
2137 Err(err) => Some(Err(err)),
2138 }
2139 } else {
2140 None
2141 }
2142 })
2143 .collect::<Result<IndexMap<String, OrderClause>, Error>>()
2144 } else {
2145 Err(Error::Query(QuerySyntaxError::InvalidOrderByProperties(
2146 "order clauses must be an array",
2147 )))
2148 }
2149 })?;
2150
2151 if !query_document.is_empty() {
2152 return Err(Error::Query(QuerySyntaxError::Unsupported(format!(
2153 "unsupported syntax in where clause: {:?}",
2154 query_document
2155 ))));
2156 }
2157
2158 Ok(DriveDocumentQuery {
2159 contract,
2160 document_type,
2161 internal_clauses,
2162 limit: Some(limit),
2163 offset,
2164 order_by,
2165 start_at,
2166 start_at_included,
2167 block_time_ms,
2168 resolved_time_ranges: vec![],
2169 sub_queries: vec![],
2170 })
2171 }
2172
2173 #[cfg(any(feature = "server", feature = "verify"))]
2174 #[allow(clippy::too_many_arguments)]
2176 pub fn from_decomposed_values(
2177 where_clause: Value,
2178 order_by: Option<Value>,
2179 maybe_limit: Option<u16>,
2180 start_at: Option<[u8; 32]>,
2181 start_at_included: bool,
2182 block_time_ms: Option<u64>,
2183 contract: &'a DataContract,
2184 document_type: DocumentTypeRef<'a>,
2185 config: &DriveConfig,
2186 platform_version: &PlatformVersion,
2187 ) -> Result<Self, Error> {
2188 let all_where_clauses: Vec<WhereClause> = match where_clause {
2189 Value::Null => Ok(vec![]),
2190 Value::Array(clauses) => clauses
2191 .iter()
2192 .map(|where_clause| {
2193 if let Value::Array(clauses_components) = where_clause {
2194 WhereClause::from_components(clauses_components)
2195 } else {
2196 Err(Error::Query(QuerySyntaxError::InvalidFormatWhereClause(
2197 "where clause must be an array".to_string(),
2198 )))
2199 }
2200 })
2201 .collect::<Result<Vec<WhereClause>, Error>>(),
2202 _ => Err(Error::Query(QuerySyntaxError::InvalidFormatWhereClause(
2203 "where clause must be an array".to_string(),
2204 ))),
2205 }?;
2206
2207 let order_by_clauses: Vec<OrderClause> = match order_by {
2215 None | Some(Value::Null) => Vec::new(),
2216 Some(Value::Array(clauses)) => clauses
2217 .iter()
2218 .map(|order_clause| match order_clause {
2219 Value::Array(components) => {
2220 OrderClause::from_components(components).map_err(|_| {
2221 Error::Query(QuerySyntaxError::InvalidOrderByProperties(
2222 "invalid order_by clause components",
2223 ))
2224 })
2225 }
2226 _ => Err(Error::Query(QuerySyntaxError::InvalidOrderByProperties(
2227 "order_by clause must be an array",
2228 ))),
2229 })
2230 .collect::<Result<Vec<_>, _>>()?,
2231 Some(_) => {
2232 return Err(Error::Query(QuerySyntaxError::InvalidOrderByProperties(
2233 "order_by must be an array",
2234 )));
2235 }
2236 };
2237
2238 Self::from_typed_clauses(
2239 all_where_clauses,
2240 order_by_clauses,
2241 maybe_limit,
2242 start_at,
2243 start_at_included,
2244 block_time_ms,
2245 contract,
2246 document_type,
2247 config,
2248 platform_version,
2249 )
2250 }
2251
2252 #[cfg(any(feature = "server", feature = "verify"))]
2272 #[allow(clippy::too_many_arguments)]
2273 pub fn from_typed_clauses(
2274 where_clauses: Vec<WhereClause>,
2275 order_by_clauses: Vec<OrderClause>,
2276 maybe_limit: Option<u16>,
2277 start_at: Option<[u8; 32]>,
2278 start_at_included: bool,
2279 block_time_ms: Option<u64>,
2280 contract: &'a DataContract,
2281 document_type: DocumentTypeRef<'a>,
2282 config: &DriveConfig,
2283 platform_version: &PlatformVersion,
2284 ) -> Result<Self, Error> {
2285 let limit = maybe_limit
2286 .map_or(Some(config.default_query_limit), |limit_value| {
2287 if limit_value == 0 || limit_value > config.default_query_limit {
2288 None
2289 } else {
2290 Some(limit_value)
2291 }
2292 })
2293 .ok_or(Error::Query(QuerySyntaxError::InvalidLimit(format!(
2294 "limit greater than max limit {}",
2295 config.max_query_limit
2296 ))))?;
2297
2298 let internal_clauses =
2299 InternalClauses::extract_from_clauses(where_clauses, platform_version)?;
2300
2301 let order_by: IndexMap<String, OrderClause> = order_by_clauses
2302 .into_iter()
2303 .map(|c| (c.field.clone(), c))
2304 .collect();
2305
2306 Ok(DriveDocumentQuery {
2307 contract,
2308 document_type,
2309 internal_clauses,
2310 offset: None,
2311 limit: Some(limit),
2312 order_by,
2313 start_at,
2314 start_at_included,
2315 block_time_ms,
2316 resolved_time_ranges: vec![],
2317 sub_queries: vec![],
2318 })
2319 }
2320
2321 #[cfg(any(feature = "server", feature = "verify"))]
2322 pub fn from_sql_expr(
2324 sql_string: &str,
2325 contract: &'a DataContract,
2326 config: Option<&DriveConfig>,
2327 platform_version: &PlatformVersion,
2328 ) -> Result<Self, Error> {
2329 let dialect: MySqlDialect = MySqlDialect {};
2330 let statements: Vec<Statement> = Parser::parse_sql(&dialect, sql_string)
2331 .map_err(|e| Error::Query(QuerySyntaxError::SQLParsingError(e)))?;
2332
2333 let first_statement =
2335 statements
2336 .first()
2337 .ok_or(Error::Query(QuerySyntaxError::InvalidSQL(
2338 "Issue parsing sql getting first statement".to_string(),
2339 )))?;
2340
2341 let query: &ast::Query = match first_statement {
2342 ast::Statement::Query(query_struct) => Some(query_struct),
2343 _ => None,
2344 }
2345 .ok_or(Error::Query(QuerySyntaxError::InvalidSQL(
2346 "Issue parsing sql: not a query".to_string(),
2347 )))?;
2348
2349 let max_limit = config
2350 .map(|config| config.max_query_limit)
2351 .unwrap_or(DriveConfig::default().max_query_limit);
2352
2353 let limit: u16 = if let Some(limit_expr) = &query.limit {
2354 match limit_expr {
2355 ast::Expr::Value(Number(num_string, _)) => {
2356 let cast_num_string: &String = num_string;
2357 let user_limit = cast_num_string.parse::<u16>().map_err(|e| {
2358 Error::Query(QuerySyntaxError::InvalidLimit(format!(
2359 "limit could not be parsed {}",
2360 e
2361 )))
2362 })?;
2363 if user_limit > max_limit {
2364 return Err(Error::Query(QuerySyntaxError::InvalidLimit(format!(
2365 "limit {} greater than max limit {}",
2366 user_limit, max_limit
2367 ))));
2368 }
2369 user_limit
2370 }
2371 result => {
2372 return Err(Error::Query(QuerySyntaxError::InvalidLimit(format!(
2373 "expression not a limit {}",
2374 result
2375 ))));
2376 }
2377 }
2378 } else {
2379 config
2380 .map(|config| config.default_query_limit)
2381 .unwrap_or(DriveConfig::default().default_query_limit)
2382 };
2383
2384 let order_by: IndexMap<String, OrderClause> = query
2385 .order_by
2386 .iter()
2387 .map(|order_exp: &OrderByExpr| {
2388 let ascending = order_exp.asc.is_none() || order_exp.asc.unwrap();
2389 let field = order_exp.expr.to_string();
2390 (field.clone(), OrderClause { field, ascending })
2391 })
2392 .collect::<IndexMap<String, OrderClause>>();
2393
2394 let select: &Select = match &*query.body {
2396 ast::SetExpr::Select(select) => Some(select),
2397 _ => None,
2398 }
2399 .ok_or(Error::Query(QuerySyntaxError::InvalidSQL(
2400 "Issue parsing sql: Not a select".to_string(),
2401 )))?;
2402
2403 let document_type_name = match &select
2405 .from
2406 .first()
2407 .ok_or(Error::Query(QuerySyntaxError::InvalidSQL(
2408 "Invalid query: missing from section".to_string(),
2409 )))?
2410 .relation
2411 {
2412 Table { name, .. } => name.0.first().as_ref().map(|identifier| &identifier.value),
2413 _ => None,
2414 }
2415 .ok_or(Error::Query(QuerySyntaxError::InvalidSQL(
2416 "Issue parsing sql: invalid from value".to_string(),
2417 )))?;
2418
2419 let document_type =
2420 contract
2421 .document_types()
2422 .get(document_type_name)
2423 .ok_or(Error::Query(QuerySyntaxError::DocumentTypeNotFound(
2424 "document type not found in contract",
2425 )))?;
2426
2427 let mut all_where_clauses: Vec<WhereClause> = Vec::new();
2437 let selection_tree = select.selection.as_ref();
2438
2439 if let Some(selection_tree) = selection_tree {
2441 WhereClause::build_where_clauses_from_operations(
2442 selection_tree,
2443 document_type,
2444 &mut all_where_clauses,
2445 )?;
2446 }
2447
2448 let internal_clauses =
2449 InternalClauses::extract_from_clauses(all_where_clauses, platform_version)?;
2450
2451 let start_at_option = None; let start_after_option = None; let mut start_at_included = true;
2454 let mut start_option: Option<Value> = None;
2455
2456 if start_after_option.is_some() {
2457 start_option = start_after_option;
2458 start_at_included = false;
2459 } else if start_at_option.is_some() {
2460 start_option = start_at_option;
2461 start_at_included = true;
2462 }
2463
2464 let start_at: Option<[u8; 32]> = start_option
2465 .map(|v| {
2466 v.into_identifier()
2467 .map_err(|e| Error::Protocol(Box::new(ProtocolError::ValueError(e))))
2468 .map(|identifier| identifier.into_buffer())
2469 })
2470 .transpose()?;
2471
2472 Ok(DriveDocumentQuery {
2473 contract,
2474 document_type: document_type.as_ref(),
2475 internal_clauses,
2476 offset: None,
2477 limit: Some(limit),
2478 order_by,
2479 start_at,
2480 start_at_included,
2481 block_time_ms: None,
2482 resolved_time_ranges: vec![],
2483 sub_queries: vec![],
2484 })
2485 }
2486
2487 #[cfg(feature = "cbor_query")]
2492 pub fn to_cbor(&self) -> Result<Vec<u8>, Error> {
2493 let data: BTreeMap<String, Value> = self.into();
2494 let cbor: BTreeMap<String, ciborium::Value> = Value::convert_to_cbor_map(data)?;
2495 let mut output = Vec::new();
2496
2497 ciborium::ser::into_writer(&cbor, &mut output)
2498 .map_err(|e| ProtocolError::PlatformSerializationError(e.to_string()))?;
2499 Ok(output)
2500 }
2501
2502 #[cfg(any(feature = "server", feature = "verify"))]
2503 pub fn start_at_document_path_and_key(&self, starts_at: &[u8; 32]) -> (Vec<Vec<u8>>, Vec<u8>) {
2505 if self.document_type.documents_keep_history() {
2506 let document_holding_path = self.contract.documents_with_history_primary_key_path(
2507 self.document_type.name().as_str(),
2508 starts_at,
2509 );
2510 (
2511 document_holding_path
2512 .into_iter()
2513 .map(|key| key.to_vec())
2514 .collect::<Vec<_>>(),
2515 vec![0],
2516 )
2517 } else {
2518 let document_holding_path = self
2519 .contract
2520 .documents_primary_key_path(self.document_type.name().as_str());
2521 (
2522 document_holding_path
2523 .into_iter()
2524 .map(|key| key.to_vec())
2525 .collect::<Vec<_>>(),
2526 starts_at.to_vec(),
2527 )
2528 }
2529 }
2530
2531 #[cfg(any(feature = "server", feature = "verify"))]
2532 pub fn validate_where_clause_shape(
2550 &self,
2551 platform_version: &PlatformVersion,
2552 ) -> Result<(), Error> {
2553 match platform_version
2554 .drive
2555 .methods
2556 .document
2557 .query
2558 .non_primary_key_path_query
2559 {
2560 0 => {
2561 if self.internal_clauses.in_clauses.len() > 1 {
2562 return Err(Error::Query(QuerySyntaxError::MultipleInClauses(
2563 "There should only be one in clause",
2564 )));
2565 }
2566 Ok(())
2567 }
2568 1 => {
2569 if self.internal_clauses.in_clauses.len() > 1 && self.start_at.is_some() {
2570 return Err(Error::Query(QuerySyntaxError::Unsupported(
2571 "startAt/startAfter is not supported with multiple in clauses".to_string(),
2572 )));
2573 }
2574 self.validate_array_element_shape(platform_version)
2575 }
2576 version => Err(Error::Drive(DriveError::UnknownVersionMismatch {
2577 method: "DriveDocumentQuery::validate_where_clause_shape".to_string(),
2578 known_versions: vec![0, 1],
2579 received: version,
2580 })),
2581 }
2582 }
2583
2584 #[cfg(any(feature = "server", feature = "verify"))]
2589 pub fn matches_several_elements(&self) -> bool {
2590 self.internal_clauses
2591 .in_clauses
2592 .iter()
2593 .any(|clause| clause.operator == WhereOperator::ContainsAny)
2594 }
2595
2596 #[cfg(any(feature = "server", feature = "verify"))]
2615 fn validate_array_element_shape(
2616 &self,
2617 platform_version: &PlatformVersion,
2618 ) -> Result<(), Error> {
2619 for clause in self
2624 .internal_clauses
2625 .equal_clauses
2626 .values()
2627 .chain(self.internal_clauses.in_clauses.iter())
2628 .chain(self.internal_clauses.range_clause.iter())
2629 {
2630 clause.validate_array_element_clause(self.document_type, platform_version)?;
2631 }
2632 if self.matches_several_elements() && !self.sub_queries.is_empty() {
2635 return Err(Error::Query(QuerySyntaxError::Unsupported(
2636 "containsAny is not supported in a composite or chained query's page: use \
2637 contains, or one page per element"
2638 .to_string(),
2639 )));
2640 }
2641 if self.matches_several_elements() && self.start_at.is_some() {
2642 return Err(Error::Query(QuerySyntaxError::Unsupported(
2643 "startAt/startAfter is not supported with containsAny: a document holding \
2644 several of the elements is under each of them, so a cursor can not place the \
2645 next page; query each element with contains to page through it"
2646 .to_string(),
2647 )));
2648 }
2649 for field in self.order_by.keys() {
2650 let walked_by_elements = self.internal_clauses.in_clauses.iter().any(|clause| {
2651 clause.field == *field && clause.operator == WhereOperator::ContainsAny
2652 });
2653 let is_typed_array =
2654 typed_array_property(self.document_type.flattened_properties(), field).is_some();
2655 if is_typed_array && !walked_by_elements {
2656 return Err(Error::Query(QuerySyntaxError::InvalidOrderByProperties(
2657 "a typed array can only be ordered by with containsAny on it, which walks \
2658 its elements in that order",
2659 )));
2660 }
2661 }
2662 Ok(())
2663 }
2664
2665 #[cfg(any(feature = "server", feature = "verify"))]
2677 pub(crate) fn drop_repeated_element_matches<T, F>(&self, rows: Vec<T>, row_key: F) -> Vec<T>
2678 where
2679 F: Fn(&T) -> Option<&[u8]>,
2680 {
2681 if !self.matches_several_elements() {
2682 return rows;
2683 }
2684 let first_occurrences: Vec<bool> = {
2685 let mut seen: HashSet<&[u8]> = HashSet::new();
2686 rows.iter()
2687 .map(|row| row_key(row).is_none_or(|bytes| seen.insert(bytes)))
2688 .collect()
2689 };
2690 rows.into_iter()
2691 .zip(first_occurrences)
2692 .filter_map(|(row, first)| first.then_some(row))
2693 .collect()
2694 }
2695
2696 #[cfg(feature = "server")]
2697 pub fn construct_path_query_operations(
2699 &self,
2700 drive: &Drive,
2701 include_start_at_for_proof: bool,
2702 transaction: TransactionArg,
2703 drive_operations: &mut Vec<LowLevelDriveOperation>,
2704 platform_version: &PlatformVersion,
2705 ) -> Result<PathQuery, Error> {
2706 self.validate_where_clause_shape(platform_version)?;
2707 {
2710 use dpp::data_contract::document_type::accessors::DocumentTypeV2Getters;
2711 if self.document_type.index_only()
2712 && self.is_for_primary_key()
2713 && !self.index_only_flat_scan_applies()
2714 {
2715 return Err(Error::Query(QuerySyntaxError::Unsupported(
2716 "indexOnly documents cannot be fetched by id: there is no primary-key \
2717 tree; query through one of the type's indexes"
2718 .to_string(),
2719 )));
2720 }
2721 if self.document_type.index_only() && self.start_at.is_some() {
2722 return Err(Error::Query(QuerySyntaxError::Unsupported(
2723 "startAt/startAfter cursors cannot address an indexOnly position (the \
2724 synthesized document id is a one-way hash of it); paginate with a \
2725 range clause on the terminal property instead — equality clauses on \
2726 the index's properties, `terminal > <last seen value>` ordered by the \
2727 terminal, and a limit"
2728 .to_string(),
2729 )));
2730 }
2731 }
2732 let drive_version = &platform_version.drive;
2733 let document_type_path = self
2735 .contract
2736 .document_type_path(self.document_type.name().as_str())
2737 .into_iter()
2738 .map(|a| a.to_vec())
2739 .collect::<Vec<Vec<u8>>>();
2740
2741 {
2746 use dpp::data_contract::document_type::accessors::DocumentTypeV2Getters;
2747 if self.document_type.index_only() {
2748 if let Some(path_query) =
2749 self.index_only_route(&document_type_path, platform_version)?
2750 {
2751 return Ok(path_query);
2752 }
2753 }
2754 }
2755
2756 let cursor_included = self.start_at_included || self.pads_cursor_page(platform_version);
2757 let (starts_at_document, start_at_path_query) = match &self.start_at {
2758 None => Ok((None, None)),
2759 Some(starts_at) => {
2760 let (start_at_document_path, start_at_document_key) =
2764 self.start_at_document_path_and_key(starts_at);
2765 let start_at_document = drive
2766 .grove_get(
2767 start_at_document_path.as_slice().into(),
2768 &start_at_document_key,
2769 StatefulQuery,
2770 transaction,
2771 drive_operations,
2772 drive_version,
2773 )
2774 .map_err(|e| match e {
2775 Error::GroveDB(e)
2776 if matches!(
2777 e.as_ref(),
2778 GroveError::PathKeyNotFound(_)
2779 | GroveError::PathNotFound(_)
2780 | GroveError::PathParentLayerNotFound(_)
2781 ) =>
2782 {
2783 let error_message = if self.start_at_included {
2784 "startAt document not found"
2785 } else {
2786 "startAfter document not found"
2787 };
2788
2789 Error::Query(QuerySyntaxError::StartDocumentNotFound(error_message))
2790 }
2791 _ => e,
2792 })?
2793 .ok_or(Error::Drive(DriveError::CorruptedCodeExecution(
2794 "expected a value",
2795 )))?;
2796
2797 let path_query =
2798 PathQuery::new_single_key(start_at_document_path, start_at_document_key);
2799
2800 let item = match platform_version
2804 .drive
2805 .methods
2806 .document
2807 .query
2808 .start_at_document
2809 {
2810 0 => match &start_at_document {
2811 Element::Item(item, _) => Some(item.as_slice()),
2812 _ => None,
2813 },
2814 1 => start_at_document.as_item_bytes().ok(),
2815 version => {
2816 return Err(Error::Drive(DriveError::UnknownVersionMismatch {
2817 method: "DriveDocumentQuery::start_at_document".to_string(),
2818 known_versions: vec![0, 1],
2819 received: version,
2820 }))
2821 }
2822 };
2823 if let Some(item) = item {
2824 let document =
2825 Document::from_bytes(item, self.document_type, platform_version)?;
2826 Ok((Some((document, cursor_included)), Some(path_query)))
2827 } else {
2828 Err(Error::Drive(DriveError::CorruptedDocumentPath(
2829 "Holding paths should only have items",
2830 )))
2831 }
2832 }
2833 }?;
2834 let mut main_path_query = if self.is_for_primary_key() {
2835 self.get_primary_key_path_query(
2836 document_type_path,
2837 starts_at_document,
2838 platform_version,
2839 )
2840 } else {
2841 self.get_non_primary_key_path_query(
2842 document_type_path,
2843 starts_at_document,
2844 platform_version,
2845 )
2846 }?;
2847 self.pad_cursor_page_limit(&mut main_path_query, platform_version)?;
2848 if !include_start_at_for_proof {
2849 return Ok(main_path_query);
2850 }
2851
2852 if let Some(mut start_at_path_query) = start_at_path_query {
2853 start_at_path_query.query.query.left_to_right =
2859 main_path_query.query.query.left_to_right;
2860 let limit = main_path_query.query.limit.take();
2861 let mut merged = PathQuery::merge(
2862 vec![&start_at_path_query, &main_path_query],
2863 &platform_version.drive.grove_version,
2864 )
2865 .map_err(Error::from)?;
2866 let cursor_on_page_layer = merged.path == main_path_query.path;
2875 let cursor_key_on_page_layer: Option<&[u8]> = if !cursor_on_page_layer {
2879 None
2880 } else if let Some(component) = start_at_path_query.path.get(merged.path.len()) {
2881 Some(component.as_slice())
2882 } else {
2883 match start_at_path_query.query.query.items.as_slice() {
2884 [QueryItem::Key(cursor_key)] => Some(cursor_key.as_slice()),
2885 _ => None,
2886 }
2887 };
2888 let cursor_row_in_page = cursor_key_on_page_layer.is_some_and(|cursor_key| {
2895 main_path_query
2896 .query
2897 .query
2898 .items
2899 .iter()
2900 .any(|item| item.contains(cursor_key))
2901 });
2902 merged.query.limit = limit.map(|a| {
2903 if cursor_row_in_page {
2904 a
2905 } else {
2906 a.saturating_add(1)
2907 }
2908 });
2909 if !cursor_on_page_layer {
2910 merged.query.query.left_to_right = true;
2932 }
2933 Ok(merged)
2934 } else {
2935 Ok(main_path_query)
2936 }
2937 }
2938
2939 #[cfg(any(feature = "server", feature = "verify"))]
2940 pub fn construct_path_query(
2942 &self,
2943 starts_at_document: Option<Document>,
2944 platform_version: &PlatformVersion,
2945 ) -> Result<PathQuery, Error> {
2946 self.validate_where_clause_shape(platform_version)?;
2947 {
2950 use dpp::data_contract::document_type::accessors::DocumentTypeV2Getters;
2951 if self.document_type.index_only()
2952 && self.is_for_primary_key()
2953 && !self.index_only_flat_scan_applies()
2954 {
2955 return Err(Error::Query(QuerySyntaxError::Unsupported(
2956 "indexOnly documents cannot be fetched by id: there is no primary-key \
2957 tree; query through one of the type's indexes"
2958 .to_string(),
2959 )));
2960 }
2961 if self.document_type.index_only() && self.start_at.is_some() {
2962 return Err(Error::Query(QuerySyntaxError::Unsupported(
2963 "startAt/startAfter cursors cannot address an indexOnly position (the \
2964 synthesized document id is a one-way hash of it); paginate with a \
2965 range clause on the terminal property instead — equality clauses on \
2966 the index's properties, `terminal > <last seen value>` ordered by the \
2967 terminal, and a limit"
2968 .to_string(),
2969 )));
2970 }
2971 }
2972 let document_type_path = self
2974 .contract
2975 .document_type_path(self.document_type.name().as_str())
2976 .into_iter()
2977 .map(|a| a.to_vec())
2978 .collect::<Vec<Vec<u8>>>();
2979
2980 {
2984 use dpp::data_contract::document_type::accessors::DocumentTypeV2Getters;
2985 if self.document_type.index_only() {
2986 if let Some(path_query) =
2987 self.index_only_route(&document_type_path, platform_version)?
2988 {
2989 return Ok(path_query);
2990 }
2991 }
2992 }
2993
2994 let cursor_included = self.start_at_included || self.pads_cursor_page(platform_version);
2995 let starts_at_document =
2996 starts_at_document.map(|starts_at_document| (starts_at_document, cursor_included));
2997 let mut path_query = if self.is_for_primary_key() {
2998 self.get_primary_key_path_query(
2999 document_type_path,
3000 starts_at_document,
3001 platform_version,
3002 )
3003 } else {
3004 self.get_non_primary_key_path_query(
3005 document_type_path,
3006 starts_at_document,
3007 platform_version,
3008 )
3009 }?;
3010 self.pad_cursor_page_limit(&mut path_query, platform_version)?;
3011 Ok(path_query)
3012 }
3013
3014 #[cfg(any(feature = "server", feature = "verify"))]
3015 pub fn pads_cursor_page(&self, platform_version: &PlatformVersion) -> bool {
3031 use dpp::data_contract::document_type::accessors::DocumentTypeV2Getters;
3032 self.start_at.is_some()
3033 && !self.start_at_included
3034 && !self.is_for_primary_key()
3035 && !self.document_type.index_only()
3036 && platform_version
3037 .drive
3038 .methods
3039 .document
3040 .query
3041 .non_primary_key_path_query
3042 >= 1
3043 }
3044
3045 #[cfg(any(feature = "server", feature = "verify"))]
3046 fn pad_cursor_page_limit(
3052 &self,
3053 path_query: &mut PathQuery,
3054 platform_version: &PlatformVersion,
3055 ) -> Result<(), Error> {
3056 if !self.pads_cursor_page(platform_version) {
3057 return Ok(());
3058 }
3059 let offset = path_query.query.offset.take().unwrap_or(0);
3060 if let Some(limit) = path_query.query.limit {
3061 let padded = limit
3062 .checked_add(1)
3063 .and_then(|limit| limit.checked_add(offset))
3064 .ok_or_else(|| {
3065 Error::Query(QuerySyntaxError::InvalidLimit(format!(
3066 "limit {limit} and offset {offset} are too large together with a \
3067 startAfter cursor"
3068 )))
3069 })?;
3070 path_query.query.limit = Some(padded);
3071 }
3072 Ok(())
3073 }
3074
3075 #[cfg(feature = "server")]
3076 pub(crate) fn strip_cursor_from_page(
3087 &self,
3088 serialized_documents: Vec<Vec<u8>>,
3089 platform_version: &PlatformVersion,
3090 ) -> Result<(Vec<Vec<u8>>, u16), Error> {
3091 let serialized_documents = self
3092 .drop_repeated_element_matches(serialized_documents, |document| {
3093 Some(document.as_slice())
3094 });
3095 self.strip_cursor_from_distinct_page(serialized_documents, platform_version)
3096 }
3097
3098 #[cfg(any(feature = "server", feature = "verify"))]
3099 pub(crate) fn strip_cursor_from_distinct_page(
3103 &self,
3104 mut serialized_documents: Vec<Vec<u8>>,
3105 platform_version: &PlatformVersion,
3106 ) -> Result<(Vec<Vec<u8>>, u16), Error> {
3107 use dpp::document::serialization_traits::DocumentPlatformConversionMethodsV0;
3108 use dpp::document::DocumentV0Getters;
3109 if !self.pads_cursor_page(platform_version) {
3110 return Ok((serialized_documents, 0));
3111 }
3112 let Some(start_at) = self.start_at else {
3113 return Ok((serialized_documents, 0));
3114 };
3115 if let Some(first) = serialized_documents.first() {
3116 let document = Document::from_bytes(first, self.document_type, platform_version)?;
3117 if document.id().to_buffer() == start_at {
3118 serialized_documents.remove(0);
3119 }
3120 }
3121 let skipped = (self.offset.unwrap_or(0) as usize).min(serialized_documents.len());
3122 serialized_documents.drain(..skipped);
3123 if let Some(limit) = self.limit {
3124 serialized_documents.truncate(limit as usize);
3125 }
3126 Ok((serialized_documents, skipped as u16))
3127 }
3128
3129 #[cfg(feature = "server")]
3130 fn strip_cursor_from_elements(
3132 &self,
3133 mut elements: QueryResultElements,
3134 platform_version: &PlatformVersion,
3135 ) -> Result<(QueryResultElements, u16), Error> {
3136 use dpp::document::DocumentV0Getters;
3137 use grovedb::query_result_type::QueryResultElement;
3138 elements.elements = self.drop_repeated_element_matches(elements.elements, |row| {
3141 let element = match row {
3142 QueryResultElement::ElementResultItem(element)
3143 | QueryResultElement::KeyElementPairResultItem((_, element))
3144 | QueryResultElement::PathKeyElementTrioResultItem((_, _, element)) => element,
3145 };
3146 element.as_item_bytes().ok()
3147 });
3148 if !self.pads_cursor_page(platform_version) {
3149 return Ok((elements, 0));
3150 }
3151 let Some(start_at) = self.start_at else {
3152 return Ok((elements, 0));
3153 };
3154 let first_element = match elements.elements.first() {
3155 Some(QueryResultElement::ElementResultItem(element))
3156 | Some(QueryResultElement::KeyElementPairResultItem((_, element)))
3157 | Some(QueryResultElement::PathKeyElementTrioResultItem((_, _, element))) => {
3158 Some(element)
3159 }
3160 None => None,
3161 };
3162 let first_is_cursor = match first_element.map(Element::as_item_bytes) {
3166 Some(Ok(bytes)) => {
3167 Document::from_bytes(bytes, self.document_type, platform_version)?
3168 .id()
3169 .to_buffer()
3170 == start_at
3171 }
3172 _ => false,
3173 };
3174 if first_is_cursor {
3175 elements.elements.remove(0);
3176 }
3177 let skipped = (self.offset.unwrap_or(0) as usize).min(elements.elements.len());
3178 elements.elements.drain(..skipped);
3179 if let Some(limit) = self.limit {
3180 elements.elements.truncate(limit as usize);
3181 }
3182 Ok((elements, skipped as u16))
3183 }
3184
3185 #[cfg(any(feature = "server", feature = "verify"))]
3186 pub fn get_primary_key_path_query(
3188 &self,
3189 document_type_path: Vec<Vec<u8>>,
3190 starts_at_document: Option<(Document, bool)>,
3191 platform_version: &PlatformVersion,
3192 ) -> Result<PathQuery, Error> {
3193 let mut path = document_type_path;
3194
3195 path.push(vec![0]);
3197
3198 if let Some(primary_key_equal_clause) = &self.internal_clauses.primary_key_equal_clause {
3199 let mut query = Query::new();
3200 let key = self.document_type.serialize_value_for_key(
3201 "$id",
3202 &primary_key_equal_clause.value,
3203 platform_version,
3204 )?;
3205 query.insert_key(key);
3206
3207 if self.document_type.documents_keep_history() {
3208 if let Some(block_time) = self.block_time_ms {
3210 let encoded_block_time = encode_u64(block_time);
3211 let mut sub_query = Query::new_with_direction(false);
3212 sub_query.insert_range_to_inclusive(..=encoded_block_time);
3213 query.set_subquery(sub_query);
3214 } else {
3215 query.set_subquery_key(vec![0]);
3216 }
3217 }
3218
3219 Ok(PathQuery::new(path, SizedQuery::new(query, Some(1), None)))
3220 } else {
3221 let left_to_right = if self.order_by.keys().len() == 1 {
3223 if self.order_by.keys().next().unwrap() != "$id" {
3224 return Err(Error::Query(QuerySyntaxError::InvalidOrderByProperties(
3225 "order by should include $id only",
3226 )));
3227 }
3228
3229 let order_clause = self.order_by.get("$id").unwrap();
3230
3231 order_clause.ascending
3232 } else {
3233 true
3234 };
3235
3236 let mut query = Query::new_with_direction(left_to_right);
3237 let starts_at_key_option = match starts_at_document {
3240 None => None,
3241 Some((document, included)) => {
3242 document
3244 .get_raw_for_document_type(
3245 "$id",
3246 self.document_type,
3247 None,
3248 platform_version,
3249 )?
3250 .map(|raw_value_option| (raw_value_option, included))
3251 }
3252 };
3253
3254 if let Some(primary_key_in_clause) = &self.internal_clauses.primary_key_in_clause {
3255 let in_values = primary_key_in_clause.in_values().into_data_with_error()??;
3256
3257 match starts_at_key_option {
3258 None => {
3259 for value in in_values.iter() {
3260 let key = self.document_type.serialize_value_for_key(
3261 "$id",
3262 value,
3263 platform_version,
3264 )?;
3265 query.insert_key(key)
3266 }
3267 }
3268 Some((starts_at_key, included)) => {
3269 for value in in_values.iter() {
3270 let key = self.document_type.serialize_value_for_key(
3271 "$id",
3272 value,
3273 platform_version,
3274 )?;
3275
3276 if (left_to_right && starts_at_key < key)
3277 || (!left_to_right && starts_at_key > key)
3278 || (included && starts_at_key == key)
3279 {
3280 query.insert_key(key);
3281 }
3282 }
3283 }
3284 }
3285
3286 if self.document_type.documents_keep_history() {
3287 if let Some(_block_time) = self.block_time_ms {
3289 return Err(Error::Query(QuerySyntaxError::Unsupported(
3291 "Not yet implemented".to_string(),
3292 )));
3293 } else {
3301 query.set_subquery_key(vec![0]);
3302 }
3303 }
3304
3305 Ok(PathQuery::new(
3306 path,
3307 SizedQuery::new(query, self.limit, self.offset),
3308 ))
3309 } else {
3310 match starts_at_key_option {
3312 None => {
3313 query.insert_all();
3314 }
3315 Some((starts_at_key, included)) => match left_to_right {
3316 true => match included {
3317 true => query.insert_range_from(starts_at_key..),
3318 false => query.insert_range_after(starts_at_key..),
3319 },
3320 false => match included {
3321 true => query.insert_range_to_inclusive(..=starts_at_key),
3322 false => query.insert_range_to(..starts_at_key),
3323 },
3324 },
3325 }
3326
3327 if self.document_type.documents_keep_history() {
3328 if let Some(_block_time) = self.block_time_ms {
3330 return Err(Error::Query(QuerySyntaxError::Unsupported(
3331 "this query is not supported".to_string(),
3332 )));
3333 } else {
3341 query.set_subquery_key(vec![0]);
3342 }
3343 }
3344
3345 Ok(PathQuery::new(
3346 path,
3347 SizedQuery::new(query, self.limit, self.offset),
3348 ))
3349 }
3350 }
3351 }
3352
3353 #[cfg(any(feature = "server", feature = "verify"))]
3354 pub fn find_best_index(&self, platform_version: &PlatformVersion) -> Result<&Index, Error> {
3369 match self.select_best_index(platform_version)? {
3370 BestIndexOutcome::Matched(index) => Ok(index),
3371 BestIndexOutcome::NoIndexMatches(no_index_error) => Err(no_index_error),
3372 }
3373 }
3374
3375 pub(crate) fn select_best_index(
3384 &self,
3385 platform_version: &PlatformVersion,
3386 ) -> Result<BestIndexOutcome<'_>, Error> {
3387 if self.resolved_time_ranges.len() > 1 {
3393 return Err(Error::Query(QuerySyntaxError::Unsupported(format!(
3394 "at most one window selection (IN_TIME_RANGE or IN_INTEGER_RANGE) is \
3395 supported per query; this one resolves {:?}, and no single index can bucket \
3396 more than one field",
3397 self.resolved_time_ranges
3398 ))));
3399 }
3400
3401 self.validate_resolved_source_shape()?;
3405
3406 if self.internal_clauses.in_clauses.len() > 1 {
3407 return Ok(BestIndexOutcome::Matched(
3411 self.find_best_index_for_multiple_in_clauses()?.0,
3412 ));
3413 }
3414
3415 let equal_fields = self
3416 .internal_clauses
3417 .equal_clauses
3418 .keys()
3419 .map(|s| s.as_str())
3420 .collect::<Vec<&str>>();
3421 let in_field = self
3422 .internal_clauses
3423 .in_clauses
3424 .first()
3425 .map(|in_clause| in_clause.field.as_str());
3426 let range_field = self
3427 .internal_clauses
3428 .range_clause
3429 .as_ref()
3430 .map(|range_clause| range_clause.field.as_str());
3431 let order_by_keys: Vec<&str> = self.order_by.keys().map(String::as_str).collect();
3432
3433 let skip_bindings = self
3439 .internal_clauses
3440 .skip_if_absent_bindings(&order_by_keys);
3441
3442 let Some((index, difference)) = self.document_type.index_for_types_matching(
3443 equal_fields.as_slice(),
3444 range_field,
3445 in_field,
3446 order_by_keys.as_slice(),
3447 |index| {
3451 document_index_admissible_for_query(
3452 index,
3453 &self.resolved_time_ranges,
3454 &skip_bindings,
3455 )
3456 },
3457 platform_version,
3458 )?
3459 else {
3460 return Ok(BestIndexOutcome::NoIndexMatches(
3461 match self.resolved_time_ranges.first() {
3462 Some(resolved) => {
3469 Error::Query(QuerySyntaxError::WhereClauseOnNonIndexedProperty(format!(
3470 "a {} query on \"{}\" requires an index that buckets it with \
3471 the resolved grid AND covers the query's other where and order-by \
3472 fields; valid indexes are: {:?}",
3473 resolved.kind(),
3474 resolved.field(),
3475 self.document_type.indexes()
3476 )))
3477 }
3478 None => {
3479 let has_bucketed_index = self
3485 .document_type
3486 .indexes()
3487 .values()
3488 .any(|index| index.is_bucketed());
3489 if has_bucketed_index {
3490 Error::Query(QuerySyntaxError::WhereClauseOnNonIndexedProperty(
3491 format!(
3492 "query must be for valid indexes, valid indexes are: {:?}; note: \
3493 this document type's bucketed (timeRange / integerRange) indexes \
3494 only serve IN_TIME_RANGE / IN_INTEGER_RANGE selections carrying \
3495 their resolution — a raw clause on the bucketed field never binds \
3496 to them",
3497 self.document_type.indexes()
3498 ),
3499 ))
3500 } else {
3501 Error::Query(QuerySyntaxError::WhereClauseOnNonIndexedProperty(
3502 format!(
3503 "query must be for valid indexes, valid indexes are: {:?}",
3504 self.document_type.indexes()
3505 ),
3506 ))
3507 }
3508 }
3509 },
3510 ));
3511 };
3512 if difference > defaults::MAX_INDEX_DIFFERENCE {
3513 return Ok(BestIndexOutcome::NoIndexMatches(Error::Query(
3514 QuerySyntaxError::QueryTooFarFromIndex("query must better match an existing index"),
3515 )));
3516 }
3517
3518 Ok(BestIndexOutcome::Matched(index))
3525 }
3526
3527 #[cfg(any(feature = "server", feature = "verify"))]
3542 pub(crate) fn validate_resolved_source_shape(&self) -> Result<(), Error> {
3543 let Some(resolved) = self.resolved_time_ranges.first() else {
3544 return Ok(());
3545 };
3546 let source = resolved.field();
3547 let has_equality_on_source = self.internal_clauses.equal_clauses.contains_key(source);
3548 let range_or_in_on_source = self
3549 .internal_clauses
3550 .range_clause
3551 .as_ref()
3552 .is_some_and(|clause| clause.field == source)
3553 || self
3554 .internal_clauses
3555 .in_clauses
3556 .iter()
3557 .any(|clause| clause.field == source);
3558 if !has_equality_on_source || range_or_in_on_source || self.order_by.contains_key(source) {
3559 return Err(Error::Query(QuerySyntaxError::Unsupported(format!(
3560 "the index on \"{source}\" buckets it into {kind} windows: it can only be \
3561 queried through a {kind} selection ({operator}, which resolves to an exact \
3562 window equality), not with ranges, IN, or ordering on that property",
3563 kind = resolved.kind(),
3564 operator = resolved.operator(),
3565 ))));
3566 }
3567 Ok(())
3568 }
3569
3570 #[cfg(any(feature = "server", feature = "verify"))]
3571 pub fn query_item_for_starts_at_key(starts_at_key: Vec<u8>, left_to_right: bool) -> QueryItem {
3573 if left_to_right {
3574 QueryItem::RangeAfter(starts_at_key..)
3575 } else {
3576 QueryItem::RangeTo(..starts_at_key)
3577 }
3578 }
3579
3580 #[cfg(any(feature = "server", feature = "verify"))]
3581 pub fn get_non_primary_key_path_query(
3588 &self,
3589 document_type_path: Vec<Vec<u8>>,
3590 starts_at_document: Option<(Document, bool)>,
3591 platform_version: &PlatformVersion,
3592 ) -> Result<PathQuery, Error> {
3593 let starts_at_document = match starts_at_document {
3594 Some((document, included)) => Some((
3595 self.cursor_with_contained_elements(
3596 self.cursor_with_derived_values(document, platform_version)?,
3597 platform_version,
3598 )?,
3599 included,
3600 )),
3601 None => None,
3602 };
3603 match platform_version
3604 .drive
3605 .methods
3606 .document
3607 .query
3608 .non_primary_key_path_query
3609 {
3610 0 => self.get_non_primary_key_path_query_v0(
3611 document_type_path,
3612 starts_at_document,
3613 platform_version,
3614 ),
3615 1 => self.get_non_primary_key_path_query_v1(
3616 document_type_path,
3617 starts_at_document,
3618 platform_version,
3619 ),
3620 version => Err(Error::Drive(DriveError::UnknownVersionMismatch {
3621 method: "DriveDocumentQuery::get_non_primary_key_path_query".to_string(),
3622 known_versions: vec![0, 1],
3623 received: version,
3624 })),
3625 }
3626 }
3627
3628 #[cfg(any(feature = "server", feature = "verify"))]
3637 fn cursor_with_derived_values(
3638 &self,
3639 mut document: Document,
3640 platform_version: &PlatformVersion,
3641 ) -> Result<Document, Error> {
3642 let derived_index_properties = self.document_type.derived_index_properties();
3643 if derived_index_properties.is_empty() {
3644 return Ok(document);
3645 }
3646 let index = self.find_best_index(platform_version)?;
3647 for property in &index.properties {
3648 if !derived_index_properties.contains_key(&property.name) {
3649 continue;
3650 }
3651 let Some(clause) = self.internal_clauses.equal_clauses.get(&property.name) else {
3652 return Err(Error::Query(QuerySyntaxError::Unsupported(format!(
3653 "a startAt or startAfter cursor is placed by the values the document it \
3654 names stores, and the index {} reads \"{}\" from the document a reference \
3655 points at instead: fix \"{}\" with == to page with a cursor, or page by a \
3656 range on another property of the index",
3657 index.name, property.name, property.name
3658 ))));
3659 };
3660 document
3661 .properties_mut()
3662 .insert(property.name.clone(), clause.value.clone());
3663 }
3664 Ok(document)
3665 }
3666
3667 #[cfg(any(feature = "server", feature = "verify"))]
3676 fn cursor_with_contained_elements(
3677 &self,
3678 mut document: Document,
3679 platform_version: &PlatformVersion,
3680 ) -> Result<Document, Error> {
3681 for clause in self.internal_clauses.equal_clauses.values() {
3682 if clause.operator != WhereOperator::Contains {
3683 continue;
3684 }
3685 let element_key = self.document_type.serialize_value_for_key(
3686 &clause.field,
3687 &clause.value,
3688 platform_version,
3689 )?;
3690 let holds_element = document
3691 .get_raw_array_elements_for_document_type(
3692 &clause.field,
3693 self.document_type,
3694 platform_version,
3695 )?
3696 .contains(&element_key);
3697 if !holds_element {
3698 return Err(Error::Query(QuerySyntaxError::Unsupported(format!(
3699 "the startAt or startAfter document does not hold the element the query \
3700 reads \"{}\" by, so it is not on the page it would place",
3701 clause.field
3702 ))));
3703 }
3704 document
3705 .properties_mut()
3706 .insert(clause.field.clone(), clause.value.clone());
3707 }
3708 Ok(document)
3709 }
3710
3711 #[cfg(feature = "server")]
3712 pub fn execute_with_proof(
3714 self,
3715 drive: &Drive,
3716 block_info: Option<BlockInfo>,
3717 transaction: TransactionArg,
3718 platform_version: &PlatformVersion,
3719 ) -> Result<(Vec<u8>, u64), Error> {
3720 self.ensure_no_sub_queries("execute_with_proof")?;
3721 let mut drive_operations = vec![];
3722 let items = self.execute_with_proof_internal(
3723 drive,
3724 transaction,
3725 &mut drive_operations,
3726 platform_version,
3727 )?;
3728 let cost = if let Some(block_info) = block_info {
3729 let fee_result = Drive::calculate_fee(
3730 None,
3731 Some(drive_operations),
3732 &block_info.epoch,
3733 drive.config.epochs_per_era,
3734 platform_version,
3735 None,
3736 )?;
3737 fee_result.processing_fee
3738 } else {
3739 0
3740 };
3741 Ok((items, cost))
3742 }
3743
3744 #[cfg(feature = "server")]
3745 pub(crate) fn execute_with_proof_internal(
3747 self,
3748 drive: &Drive,
3749 transaction: TransactionArg,
3750 drive_operations: &mut Vec<LowLevelDriveOperation>,
3751 platform_version: &PlatformVersion,
3752 ) -> Result<Vec<u8>, Error> {
3753 let path_query = self.construct_path_query_operations(
3754 drive,
3755 true,
3756 transaction,
3757 drive_operations,
3758 platform_version,
3759 )?;
3760 drive.grove_get_proved_path_query(
3761 &path_query,
3762 transaction,
3763 drive_operations,
3764 &platform_version.drive,
3765 )
3766 }
3767
3768 #[cfg(all(feature = "server", feature = "verify"))]
3769 pub fn execute_with_proof_only_get_elements(
3771 self,
3772 drive: &Drive,
3773 block_info: Option<BlockInfo>,
3774 transaction: TransactionArg,
3775 platform_version: &PlatformVersion,
3776 ) -> Result<(RootHash, Vec<Vec<u8>>, u64), Error> {
3777 self.ensure_no_sub_queries("execute_with_proof_only_get_elements")?;
3778 let mut drive_operations = vec![];
3779 let (root_hash, items) = self.execute_with_proof_only_get_elements_internal(
3780 drive,
3781 transaction,
3782 &mut drive_operations,
3783 platform_version,
3784 )?;
3785 let cost = if let Some(block_info) = block_info {
3786 let fee_result = Drive::calculate_fee(
3787 None,
3788 Some(drive_operations),
3789 &block_info.epoch,
3790 drive.config.epochs_per_era,
3791 platform_version,
3792 None,
3793 )?;
3794 fee_result.processing_fee
3795 } else {
3796 0
3797 };
3798 Ok((root_hash, items, cost))
3799 }
3800
3801 #[cfg(all(feature = "server", feature = "verify"))]
3802 pub(crate) fn execute_with_proof_only_get_elements_internal(
3804 self,
3805 drive: &Drive,
3806 transaction: TransactionArg,
3807 drive_operations: &mut Vec<LowLevelDriveOperation>,
3808 platform_version: &PlatformVersion,
3809 ) -> Result<(RootHash, Vec<Vec<u8>>), Error> {
3810 let path_query = self.construct_path_query_operations(
3811 drive,
3812 true,
3813 transaction,
3814 drive_operations,
3815 platform_version,
3816 )?;
3817
3818 let proof = drive.grove_get_proved_path_query(
3819 &path_query,
3820 transaction,
3821 drive_operations,
3822 &platform_version.drive,
3823 )?;
3824 self.verify_proof_keep_serialized(proof.as_slice(), platform_version)
3825 }
3826
3827 #[cfg(feature = "server")]
3828 pub fn execute_raw_results_no_proof(
3830 &self,
3831 drive: &Drive,
3832 block_info: Option<BlockInfo>,
3833 transaction: TransactionArg,
3834 platform_version: &PlatformVersion,
3835 ) -> Result<(Vec<Vec<u8>>, u16, u64), Error> {
3836 self.ensure_no_sub_queries("execute_raw_results_no_proof")?;
3837 let mut drive_operations = vec![];
3838 let (items, skipped) = self.execute_raw_results_no_proof_internal(
3839 drive,
3840 transaction,
3841 &mut drive_operations,
3842 platform_version,
3843 )?;
3844 let cost = if let Some(block_info) = block_info {
3845 let fee_result = Drive::calculate_fee(
3846 None,
3847 Some(drive_operations),
3848 &block_info.epoch,
3849 drive.config.epochs_per_era,
3850 platform_version,
3851 None,
3852 )?;
3853 fee_result.processing_fee
3854 } else {
3855 0
3856 };
3857 Ok((items, skipped, cost))
3858 }
3859
3860 #[cfg(feature = "server")]
3861 pub(crate) fn refuse_an_uncovered_index_only_projection(
3877 &self,
3878 platform_version: &PlatformVersion,
3879 ) -> Result<(), Error> {
3880 if !self.document_type.index_only() {
3881 return Ok(());
3882 }
3883 if (self.is_for_primary_key() && !self.index_only_flat_scan_applies())
3890 || self.start_at.is_some()
3891 {
3892 return Ok(());
3893 }
3894 let index = self.index_only_query_index(platform_version)?;
3895 let covers_every_property = self
3896 .document_type
3897 .flattened_properties()
3898 .iter()
3899 .filter(|(_, property)| {
3900 !matches!(property.property_type, DocumentPropertyType::Object(_))
3901 })
3902 .all(|(name, _)| {
3903 index.terminal_contains(name)
3904 || self.document_type.entry_payload().contains(name.as_str())
3905 || index
3906 .properties
3907 .iter()
3908 .any(|index_property| index_property.name == *name)
3909 });
3910 let covers_required_system_properties = self
3916 .document_type
3917 .required_fields()
3918 .iter()
3919 .filter(|name| {
3920 name.starts_with('$')
3921 && name.as_str() != document::property_names::ID
3922 && name.as_str() != document::property_names::OWNER_ID
3923 })
3924 .all(|name| {
3925 name.as_str() == document::property_names::CREATED_AT
3926 && index.involves(document::property_names::CREATED_AT)
3927 });
3928 if !covers_required_system_properties {
3929 return Err(Error::Query(uncovered_required_property_refusal()));
3930 }
3931 if covers_every_property {
3932 return Ok(());
3933 }
3934 Err(Error::Query(QuerySyntaxError::Unsupported(
3935 "this indexOnly query's index does not cover every property, so the documents it \
3936 synthesizes cannot be serialized into a non-proof response; query through an \
3937 index covering all properties, or use a proved query"
3938 .to_string(),
3939 )))
3940 }
3941
3942 #[cfg(feature = "server")]
3943 pub(crate) fn execute_raw_results_no_proof_internal(
3945 &self,
3946 drive: &Drive,
3947 transaction: TransactionArg,
3948 drive_operations: &mut Vec<LowLevelDriveOperation>,
3949 platform_version: &PlatformVersion,
3950 ) -> Result<(Vec<Vec<u8>>, u16), Error> {
3951 {
3958 use dpp::data_contract::document_type::accessors::DocumentTypeV2Getters;
3959 if self.document_type.index_only() {
3960 self.refuse_an_uncovered_index_only_projection(platform_version)?;
3961 let (documents, skipped) = self.execute_index_only_documents_no_proof_internal(
3962 drive,
3963 transaction,
3964 drive_operations,
3965 platform_version,
3966 )?;
3967 let serialized = documents
3968 .into_iter()
3969 .map(|document| {
3970 document
3971 .serialize(self.document_type, self.contract, platform_version)
3972 .map_err(|error| match index_only_serialization_refusal(&error) {
3973 Some(refusal) => Error::Query(refusal),
3974 None => error.into(),
3975 })
3976 })
3977 .collect::<Result<Vec<_>, Error>>()?;
3978 return Ok((serialized, skipped));
3979 }
3980 }
3981
3982 let path_query = self.construct_path_query_operations(
3983 drive,
3984 false,
3985 transaction,
3986 drive_operations,
3987 platform_version,
3988 )?;
3989
3990 let query_result = drive.grove_get_path_query_serialized_results(
3991 &path_query,
3992 transaction,
3993 drive_operations,
3994 &platform_version.drive,
3995 );
3996 match query_result {
3997 Err(Error::GroveDB(e))
3998 if matches!(
3999 e.as_ref(),
4000 GroveError::PathKeyNotFound(_)
4001 | GroveError::PathNotFound(_)
4002 | GroveError::PathParentLayerNotFound(_)
4003 ) =>
4004 {
4005 Ok((Vec::new(), 0))
4006 }
4007 _ => {
4008 let (data, skipped) = query_result?;
4009 let (data, cursor_skipped) = self.strip_cursor_from_page(data, platform_version)?;
4010 Ok((data, skipped.saturating_add(cursor_skipped)))
4011 }
4012 }
4013 }
4014
4015 #[cfg(feature = "server")]
4016 pub(crate) fn execute_no_proof_internal(
4018 &self,
4019 drive: &Drive,
4020 result_type: QueryResultType,
4021 transaction: TransactionArg,
4022 drive_operations: &mut Vec<LowLevelDriveOperation>,
4023 platform_version: &PlatformVersion,
4024 ) -> Result<(QueryResultElements, u16), Error> {
4025 let path_query = self.construct_path_query_operations(
4026 drive,
4027 false,
4028 transaction,
4029 drive_operations,
4030 platform_version,
4031 )?;
4032 let query_result = drive.grove_get_path_query(
4033 &path_query,
4034 transaction,
4035 result_type,
4036 drive_operations,
4037 &platform_version.drive,
4038 );
4039 match query_result {
4040 Err(Error::GroveDB(e))
4041 if matches!(
4042 e.as_ref(),
4043 GroveError::PathKeyNotFound(_)
4044 | GroveError::PathNotFound(_)
4045 | GroveError::PathParentLayerNotFound(_)
4046 ) =>
4047 {
4048 Ok((QueryResultElements::new(), 0))
4049 }
4050 _ => {
4051 let (data, skipped) = query_result?;
4052 let (data, cursor_skipped) =
4053 self.strip_cursor_from_elements(data, platform_version)?;
4054 Ok((data, skipped.saturating_add(cursor_skipped)))
4055 }
4056 }
4057 }
4058}
4059
4060impl<'a> From<&DriveDocumentQuery<'a>> for BTreeMap<String, Value> {
4062 fn from(query: &DriveDocumentQuery<'a>) -> Self {
4063 let mut response = BTreeMap::<String, Value>::new();
4064
4065 response.insert(
4068 "contract_id".to_string(),
4069 Value::Identifier(query.contract.id().to_buffer()),
4070 );
4071
4072 response.insert(
4075 "document_type_name".to_string(),
4076 Value::Text(query.document_type.name().to_string()),
4077 );
4078
4079 let all_where_clauses: Vec<WhereClause> = query.internal_clauses.clone().into();
4081 response.insert(
4082 "where".to_string(),
4083 Value::Array(all_where_clauses.into_iter().map(|v| v.into()).collect()),
4084 );
4085
4086 if let Some(offset) = query.offset {
4088 response.insert("offset".to_string(), Value::U16(offset));
4089 };
4090 if let Some(limit) = query.limit {
4092 response.insert("limit".to_string(), Value::U16(limit));
4093 };
4094 let order_by = &query.order_by;
4096 let value: Vec<Value> = order_by
4097 .into_iter()
4098 .map(|(_k, v)| v.clone().into())
4099 .collect();
4100 response.insert("orderBy".to_string(), Value::Array(value));
4101
4102 if let Some(start_at) = query.start_at {
4104 let v = Value::Identifier(start_at);
4105 if query.start_at_included {
4106 response.insert("startAt".to_string(), v);
4107 } else {
4108 response.insert("startAfter".to_string(), v);
4109 }
4110 };
4111
4112 if let Some(block_time_ms) = query.block_time_ms {
4114 response.insert("blockTime".to_string(), Value::U64(block_time_ms));
4115 };
4116
4117 response
4118 }
4119}
4120
4121#[cfg(all(test, feature = "server"))]
4122pub(crate) mod unsigned_index_key_order_tests;
4123
4124#[cfg(feature = "server")]
4125#[cfg(test)]
4126mod tests {
4127
4128 use dpp::data_contract::document_type::accessors::DocumentTypeV0Getters;
4129
4130 use dpp::prelude::Identifier;
4131 use grovedb::Query;
4132 use indexmap::IndexMap;
4133 use rand::prelude::StdRng;
4134 use rand::SeedableRng;
4135 use serde_json::json;
4136 use std::borrow::Cow;
4137 use std::collections::BTreeMap;
4138 use std::option::Option::None;
4139
4140 use crate::drive::Drive;
4141 use crate::query::{
4142 DriveDocumentQuery, InternalClauses, OrderClause, WhereClause, WhereOperator,
4143 };
4144 use crate::util::storage_flags::StorageFlags;
4145
4146 use dpp::data_contract::DataContract;
4147
4148 use serde_json::Value::Null;
4149
4150 use crate::config::DriveConfig;
4151 use crate::util::test_helpers::setup::{setup_drive, setup_drive_with_initial_state_structure};
4152 use dpp::block::block_info::BlockInfo;
4153 use dpp::data_contract::accessors::v0::DataContractV0Getters;
4154 use dpp::data_contracts::SystemDataContract;
4155 use dpp::document::DocumentV0;
4156 use dpp::platform_value::string_encoding::Encoding;
4157 use dpp::platform_value::Value;
4158 use dpp::system_data_contracts::load_system_data_contract;
4159 use dpp::tests::fixtures::{get_data_contract_fixture, get_dpns_data_contract_fixture};
4160 use dpp::tests::json_document::json_document_to_contract;
4161 use dpp::util::cbor_serializer;
4162 use dpp::version::PlatformVersion;
4163
4164 fn setup_family_contract() -> (Drive, DataContract) {
4165 let platform_version = PlatformVersion::latest();
4166
4167 let drive = setup_drive(None);
4168
4169 drive
4170 .create_initial_state_structure(None, platform_version)
4171 .expect("expected to create root tree successfully");
4172
4173 let contract_path = "tests/supporting_files/contract/family/family-contract.json";
4174
4175 let contract = json_document_to_contract(contract_path, false, platform_version)
4177 .expect("expected to get document");
4178
4179 let storage_flags = Some(Cow::Owned(StorageFlags::SingleEpoch(0)));
4180 drive
4181 .apply_contract(
4182 &contract,
4183 BlockInfo::default(),
4184 true,
4185 storage_flags,
4186 None,
4187 platform_version,
4188 )
4189 .expect("expected to apply contract successfully");
4190
4191 (drive, contract)
4192 }
4193
4194 fn setup_withdrawal_contract() -> (Drive, DataContract) {
4195 let platform_version = PlatformVersion::latest();
4196
4197 let drive = setup_drive(None);
4198
4199 drive
4200 .create_initial_state_structure(None, platform_version)
4201 .expect("expected to create root tree successfully");
4202
4203 let contract = load_system_data_contract(SystemDataContract::Withdrawals, platform_version)
4205 .expect("load system contact");
4206
4207 let storage_flags = Some(Cow::Owned(StorageFlags::SingleEpoch(0)));
4208 drive
4209 .apply_contract(
4210 &contract,
4211 BlockInfo::default(),
4212 true,
4213 storage_flags,
4214 None,
4215 platform_version,
4216 )
4217 .expect("expected to apply contract successfully");
4218
4219 (drive, contract)
4220 }
4221
4222 fn setup_family_birthday_contract() -> (Drive, DataContract) {
4223 let drive = setup_drive_with_initial_state_structure(None);
4224
4225 let platform_version = PlatformVersion::latest();
4226
4227 let contract_path =
4228 "tests/supporting_files/contract/family/family-contract-with-birthday.json";
4229
4230 let contract = json_document_to_contract(contract_path, false, platform_version)
4232 .expect("expected to get document");
4233 let storage_flags = Some(Cow::Owned(StorageFlags::SingleEpoch(0)));
4234 drive
4235 .apply_contract(
4236 &contract,
4237 BlockInfo::default(),
4238 true,
4239 storage_flags,
4240 None,
4241 platform_version,
4242 )
4243 .expect("expected to apply contract successfully");
4244
4245 (drive, contract)
4246 }
4247
4248 #[test]
4249 fn test_drive_query_from_to_cbor() {
4250 let config = DriveConfig::default();
4251 let contract = get_data_contract_fixture(None, 0, 1).data_contract_owned();
4252 let document_type = contract
4253 .document_type_for_name("niceDocument")
4254 .expect("expected to get nice document");
4255 let start_after = Identifier::random();
4256
4257 let query_value = json!({
4258 "contract_id": contract.id(),
4259 "document_type_name": document_type.name(),
4260 "where": [
4261 ["firstName", "<", "Gilligan"],
4262 ["lastName", "=", "Doe"]
4263 ],
4264 "limit": 100u16,
4265 "offset": 10u16,
4266 "orderBy": [
4267 ["firstName", "asc"],
4268 ["lastName", "desc"],
4269 ],
4270 "startAfter": start_after,
4271 "blockTime": 13453432u64,
4272 });
4273
4274 let where_cbor = cbor_serializer::serializable_value_to_cbor(&query_value, None)
4275 .expect("expected to serialize to cbor");
4276 let query = DriveDocumentQuery::from_cbor(
4277 where_cbor.as_slice(),
4278 &contract,
4279 document_type,
4280 &config,
4281 PlatformVersion::latest(),
4282 )
4283 .expect("deserialize cbor shouldn't fail");
4284
4285 let cbor = query.to_cbor().expect("should serialize cbor");
4286
4287 let deserialized = DriveDocumentQuery::from_cbor(
4288 &cbor,
4289 &contract,
4290 document_type,
4291 &config,
4292 PlatformVersion::latest(),
4293 )
4294 .expect("should deserialize cbor");
4295
4296 assert_eq!(query, deserialized);
4297
4298 assert_eq!(deserialized.start_at, Some(start_after.to_buffer()));
4299 assert!(!deserialized.start_at_included);
4300 assert_eq!(deserialized.block_time_ms, Some(13453432u64));
4301 }
4302
4303 #[test]
4304 fn test_invalid_query_ranges_different_fields() {
4305 let query_value = json!({
4306 "where": [
4307 ["firstName", "<", "Gilligan"],
4308 ["lastName", "<", "Michelle"],
4309 ],
4310 "limit": 100,
4311 "orderBy": [
4312 ["firstName", "asc"],
4313 ["lastName", "asc"],
4314 ]
4315 });
4316 let contract = get_data_contract_fixture(None, 0, 1).data_contract_owned();
4317 let document_type = contract
4318 .document_type_for_name("niceDocument")
4319 .expect("expected to get nice document");
4320
4321 let where_cbor = cbor_serializer::serializable_value_to_cbor(&query_value, None)
4322 .expect("expected to serialize to cbor");
4323 DriveDocumentQuery::from_cbor(
4324 where_cbor.as_slice(),
4325 &contract,
4326 document_type,
4327 &DriveConfig::default(),
4328 PlatformVersion::latest(),
4329 )
4330 .expect_err("all ranges must be on same field");
4331 }
4332
4333 #[test]
4334 fn test_invalid_query_extra_invalid_field() {
4335 let query_value = json!({
4336 "where": [
4337 ["firstName", "<", "Gilligan"],
4338 ],
4339 "limit": 100,
4340 "orderBy": [
4341 ["firstName", "asc"],
4342 ["lastName", "asc"],
4343 ],
4344 "invalid": 0,
4345 });
4346 let contract = get_data_contract_fixture(None, 0, 1).data_contract_owned();
4347 let document_type = contract
4348 .document_type_for_name("niceDocument")
4349 .expect("expected to get nice document");
4350
4351 let where_cbor = cbor_serializer::serializable_value_to_cbor(&query_value, None)
4352 .expect("expected to serialize to cbor");
4353 DriveDocumentQuery::from_cbor(
4354 where_cbor.as_slice(),
4355 &contract,
4356 document_type,
4357 &DriveConfig::default(),
4358 PlatformVersion::latest(),
4359 )
4360 .expect_err("fields of queries must of defined supported types (where, limit, orderBy...)");
4361 }
4362
4363 #[test]
4364 fn test_invalid_query_conflicting_clauses() {
4365 let query_value = json!({
4366 "where": [
4367 ["firstName", "<", "Gilligan"],
4368 ["firstName", ">", "Gilligan"],
4369 ],
4370 "limit": 100,
4371 "orderBy": [
4372 ["firstName", "asc"],
4373 ["lastName", "asc"],
4374 ],
4375 });
4376
4377 let contract = get_data_contract_fixture(None, 0, 1).data_contract_owned();
4378 let document_type = contract
4379 .document_type_for_name("niceDocument")
4380 .expect("expected to get nice document");
4381
4382 let where_cbor = cbor_serializer::serializable_value_to_cbor(&query_value, None)
4383 .expect("expected to serialize to cbor");
4384 DriveDocumentQuery::from_cbor(
4385 where_cbor.as_slice(),
4386 &contract,
4387 document_type,
4388 &DriveConfig::default(),
4389 PlatformVersion::latest(),
4390 )
4391 .expect_err("the query should not be created");
4392 }
4393
4394 #[test]
4395 fn test_valid_query_groupable_meeting_clauses() {
4396 let query_value = json!({
4397 "where": [
4398 ["firstName", "<=", "Gilligan"],
4399 ["firstName", ">", "Gilligan"],
4400 ],
4401 "limit": 100,
4402 "orderBy": [
4403 ["firstName", "asc"],
4404 ["lastName", "asc"],
4405 ],
4406 });
4407
4408 let contract = get_data_contract_fixture(None, 0, 1).data_contract_owned();
4409 let document_type = contract
4410 .document_type_for_name("niceDocument")
4411 .expect("expected to get nice document");
4412
4413 let where_cbor = cbor_serializer::serializable_value_to_cbor(&query_value, None)
4414 .expect("expected to serialize to cbor");
4415 DriveDocumentQuery::from_cbor(
4416 where_cbor.as_slice(),
4417 &contract,
4418 document_type,
4419 &DriveConfig::default(),
4420 PlatformVersion::latest(),
4421 )
4422 .expect("the query should be created");
4423 }
4424
4425 #[test]
4426 fn test_valid_query_query_field_at_max_length() {
4427 let long_string = "t".repeat(255);
4428 let query_value = json!({
4429 "where": [
4430 ["firstName", "<", long_string],
4431 ],
4432 "limit": 100,
4433 "orderBy": [
4434 ["firstName", "asc"],
4435 ["lastName", "asc"],
4436 ],
4437 });
4438 let contract = get_data_contract_fixture(None, 0, 1).data_contract_owned();
4439 let document_type = contract
4440 .document_type_for_name("niceDocument")
4441 .expect("expected to get nice document");
4442
4443 let where_cbor = cbor_serializer::serializable_value_to_cbor(&query_value, None)
4444 .expect("expected to serialize to cbor");
4445 DriveDocumentQuery::from_cbor(
4446 where_cbor.as_slice(),
4447 &contract,
4448 document_type,
4449 &DriveConfig::default(),
4450 PlatformVersion::latest(),
4451 )
4452 .expect("query should be fine for a 255 byte long string");
4453 }
4454
4455 #[test]
4456 fn test_valid_query_drive_document_query() {
4457 let platform_version = PlatformVersion::latest();
4458 let mut rng = StdRng::seed_from_u64(5);
4459 let contract =
4460 get_dpns_data_contract_fixture(Some(Identifier::random_with_rng(&mut rng)), 0, 1)
4461 .data_contract_owned();
4462 let domain = contract
4463 .document_type_for_name("domain")
4464 .expect("expected to get domain");
4465
4466 let query_asc = DriveDocumentQuery {
4467 contract: &contract,
4468 document_type: domain,
4469 internal_clauses: InternalClauses {
4470 primary_key_in_clause: None,
4471 primary_key_equal_clause: None,
4472 in_clauses: Vec::new(),
4473 range_clause: Some(WhereClause {
4474 field: "records.identity".to_string(),
4475 operator: WhereOperator::LessThan,
4476 value: Value::Identifier(
4477 Identifier::from_string(
4478 "AYN4srupPWDrp833iG5qtmaAsbapNvaV7svAdncLN5Rh",
4479 Encoding::Base58,
4480 )
4481 .unwrap()
4482 .to_buffer(),
4483 ),
4484 }),
4485 equal_clauses: BTreeMap::new(),
4486 },
4487 offset: None,
4488 limit: Some(6),
4489 order_by: vec![(
4490 "records.identity".to_string(),
4491 OrderClause {
4492 field: "records.identity".to_string(),
4493 ascending: false,
4494 },
4495 )]
4496 .into_iter()
4497 .collect(),
4498 start_at: None,
4499 start_at_included: false,
4500 block_time_ms: None,
4501 resolved_time_ranges: vec![],
4502 sub_queries: vec![],
4503 };
4504
4505 let path_query = query_asc
4506 .construct_path_query(None, platform_version)
4507 .expect("expected to create path query");
4508
4509 assert_eq!(path_query.to_string(), "PathQuery { path: [@, 0x1da29f488023e306ff9a680bc9837153fb0778c8ee9c934a87dc0de1d69abd3c, 0x01, domain, 0x7265636f7264732e6964656e74697479], query: SizedQuery { query: Query {\n items: [\n RangeTo(.. 0x8dc201fd7ad7905f8a84d66218e2b387daea7fe4739ae0e21e8c3ee755e6a2c0),\n ],\n default_subquery_branch: SubqueryBranch { subquery_path: [0x00], subquery: Query {\n items: [\n RangeFull,\n ],\n default_subquery_branch: SubqueryBranch { subquery_path: None subquery: None },\n left_to_right: false,\n add_parent_tree_on_subquery: false,\n} },\n conditional_subquery_branches: {\n Key(): SubqueryBranch { subquery_path: [0x00], subquery: Query {\n items: [\n RangeFull,\n ],\n default_subquery_branch: SubqueryBranch { subquery_path: None subquery: None },\n left_to_right: false,\n add_parent_tree_on_subquery: false,\n} },\n },\n left_to_right: false,\n add_parent_tree_on_subquery: false,\n}, limit: 6 } }");
4510
4511 let encoded = bincode::encode_to_vec(&path_query, bincode::config::standard())
4513 .expect("Failed to serialize PathQuery");
4514
4515 let hex_string = hex::encode(encoded);
4517
4518 assert_eq!(hex_string, "050140201da29f488023e306ff9a680bc9837153fb0778c8ee9c934a87dc0de1d69abd3c010106646f6d61696e107265636f7264732e6964656e74697479010105208dc201fd7ad7905f8a84d66218e2b387daea7fe4739ae0e21e8c3ee755e6a2c00101010001010103000000000001010000010101000101010300000000000000010600");
4522 }
4523
4524 #[test]
4525 fn test_invalid_query_field_too_long() {
4526 let (drive, contract) = setup_family_contract();
4527
4528 let platform_version = PlatformVersion::latest();
4529
4530 let document_type = contract
4531 .document_type_for_name("person")
4532 .expect("expected to get a document type");
4533
4534 let too_long_string = "t".repeat(256);
4535 let query_value = json!({
4536 "where": [
4537 ["firstName", "<", too_long_string],
4538 ],
4539 "limit": 100,
4540 "orderBy": [
4541 ["firstName", "asc"],
4542 ["lastName", "asc"],
4543 ],
4544 });
4545
4546 let where_cbor = cbor_serializer::serializable_value_to_cbor(&query_value, None)
4547 .expect("expected to serialize to cbor");
4548 let query = DriveDocumentQuery::from_cbor(
4549 where_cbor.as_slice(),
4550 &contract,
4551 document_type,
4552 &DriveConfig::default(),
4553 PlatformVersion::latest(),
4554 )
4555 .expect("fields of queries length must be under 256 bytes long");
4556 query
4557 .execute_raw_results_no_proof(&drive, None, None, platform_version)
4558 .expect_err("fields of queries length must be under 256 bytes long");
4559 }
4560
4561 #[test]
4617 fn test_valid_query_timestamp_field_with_null_value() {
4618 let (drive, contract) = setup_family_birthday_contract();
4619
4620 let platform_version = PlatformVersion::latest();
4621
4622 let document_type = contract
4623 .document_type_for_name("person")
4624 .expect("expected to get a document type");
4625
4626 let query_value = json!({
4627 "where": [
4628 ["birthday", ">=", Null],
4629 ],
4630 "limit": 100,
4631 "orderBy": [
4632 ["birthday", "asc"],
4633 ],
4634 });
4635
4636 let where_cbor = cbor_serializer::serializable_value_to_cbor(&query_value, None)
4637 .expect("expected to serialize to cbor");
4638 let query = DriveDocumentQuery::from_cbor(
4639 where_cbor.as_slice(),
4640 &contract,
4641 document_type,
4642 &DriveConfig::default(),
4643 PlatformVersion::latest(),
4644 )
4645 .expect("The query itself should be valid for a null type");
4646 query
4647 .execute_raw_results_no_proof(&drive, None, None, platform_version)
4648 .expect("a Null value doesn't make sense for a float");
4649 }
4650
4651 #[test]
4652 fn test_invalid_query_in_with_empty_array() {
4653 let (drive, contract) = setup_family_contract();
4654
4655 let platform_version = PlatformVersion::latest();
4656
4657 let document_type = contract
4658 .document_type_for_name("person")
4659 .expect("expected to get a document type");
4660
4661 let query_value = json!({
4662 "where": [
4663 ["firstName", "in", []],
4664 ],
4665 "limit": 100,
4666 "orderBy": [
4667 ["firstName", "asc"],
4668 ],
4669 });
4670
4671 let where_cbor = cbor_serializer::serializable_value_to_cbor(&query_value, None)
4672 .expect("expected to serialize to cbor");
4673 let query = DriveDocumentQuery::from_cbor(
4674 where_cbor.as_slice(),
4675 &contract,
4676 document_type,
4677 &DriveConfig::default(),
4678 PlatformVersion::latest(),
4679 )
4680 .expect("query should be valid for empty array");
4681
4682 query
4683 .execute_raw_results_no_proof(&drive, None, None, platform_version)
4684 .expect_err("query should not be able to execute for empty array");
4685 }
4686
4687 #[test]
4688 fn test_invalid_query_in_too_many_elements() {
4689 let (drive, contract) = setup_family_contract();
4690
4691 let platform_version = PlatformVersion::latest();
4692
4693 let document_type = contract
4694 .document_type_for_name("person")
4695 .expect("expected to get a document type");
4696
4697 let mut array: Vec<String> = Vec::with_capacity(101);
4698 for _ in 0..array.capacity() {
4699 array.push(String::from("a"));
4700 }
4701 let query_value = json!({
4702 "where": [
4703 ["firstName", "in", array],
4704 ],
4705 "limit": 100,
4706 "orderBy": [
4707 ["firstName", "asc"],
4708 ],
4709 });
4710
4711 let where_cbor = cbor_serializer::serializable_value_to_cbor(&query_value, None)
4712 .expect("expected to serialize to cbor");
4713 let query = DriveDocumentQuery::from_cbor(
4714 where_cbor.as_slice(),
4715 &contract,
4716 document_type,
4717 &DriveConfig::default(),
4718 PlatformVersion::latest(),
4719 )
4720 .expect("query is valid for too many elements");
4721
4722 query
4723 .execute_raw_results_no_proof(&drive, None, None, platform_version)
4724 .expect_err("query should not be able to execute with too many elements");
4725 }
4726
4727 #[test]
4728 fn test_invalid_query_in_unique_elements() {
4729 let (drive, contract) = setup_family_contract();
4730
4731 let platform_version = PlatformVersion::latest();
4732
4733 let document_type = contract
4734 .document_type_for_name("person")
4735 .expect("expected to get a document type");
4736
4737 let query_value = json!({
4738 "where": [
4739 ["firstName", "in", ["a", "a"]],
4740 ],
4741 "limit": 100,
4742 "orderBy": [
4743 ["firstName", "asc"],
4744 ],
4745 });
4746
4747 let where_cbor = cbor_serializer::serializable_value_to_cbor(&query_value, None)
4748 .expect("expected to serialize to cbor");
4749
4750 let query = DriveDocumentQuery::from_cbor(
4754 where_cbor.as_slice(),
4755 &contract,
4756 document_type,
4757 &DriveConfig::default(),
4758 PlatformVersion::latest(),
4759 )
4760 .expect("the query should be created");
4761
4762 query
4763 .execute_raw_results_no_proof(&drive, None, None, platform_version)
4764 .expect_err("there should be no duplicates values for In query");
4765 }
4766
4767 #[test]
4768 fn test_invalid_query_starts_with_empty_string() {
4769 let query_value = json!({
4770 "where": [
4771 ["firstName", "startsWith", ""],
4772 ],
4773 "limit": 100,
4774 "orderBy": [
4775 ["firstName", "asc"],
4776 ],
4777 });
4778
4779 let contract = get_data_contract_fixture(None, 0, 1).data_contract_owned();
4780 let document_type = contract
4781 .document_type_for_name("niceDocument")
4782 .expect("expected to get nice document");
4783
4784 let where_cbor = cbor_serializer::serializable_value_to_cbor(&query_value, None)
4785 .expect("expected to serialize to cbor");
4786 DriveDocumentQuery::from_cbor(
4787 where_cbor.as_slice(),
4788 &contract,
4789 document_type,
4790 &DriveConfig::default(),
4791 PlatformVersion::latest(),
4792 )
4793 .expect_err("starts with can not start with an empty string");
4794 }
4795
4796 #[test]
4797 fn test_invalid_query_limit_too_high() {
4798 let query_value = json!({
4799 "where": [
4800 ["firstName", "startsWith", "a"],
4801 ],
4802 "limit": 101,
4803 "orderBy": [
4804 ["firstName", "asc"],
4805 ],
4806 });
4807
4808 let contract = get_data_contract_fixture(None, 0, 1).data_contract_owned();
4809 let document_type = contract
4810 .document_type_for_name("niceDocument")
4811 .expect("expected to get nice document");
4812
4813 let where_cbor = cbor_serializer::serializable_value_to_cbor(&query_value, None)
4814 .expect("expected to serialize to cbor");
4815 DriveDocumentQuery::from_cbor(
4816 where_cbor.as_slice(),
4817 &contract,
4818 document_type,
4819 &DriveConfig::default(),
4820 PlatformVersion::latest(),
4821 )
4822 .expect_err("starts with can not start with an empty string");
4823 }
4824
4825 #[test]
4826 fn test_invalid_query_limit_too_low() {
4827 let query_value = json!({
4828 "where": [
4829 ["firstName", "startsWith", "a"],
4830 ],
4831 "limit": -1,
4832 "orderBy": [
4833 ["firstName", "asc"],
4834 ],
4835 });
4836
4837 let contract = get_data_contract_fixture(None, 0, 1).data_contract_owned();
4838 let document_type = contract
4839 .document_type_for_name("niceDocument")
4840 .expect("expected to get nice document");
4841
4842 let where_cbor = cbor_serializer::serializable_value_to_cbor(&query_value, None)
4843 .expect("expected to serialize to cbor");
4844 DriveDocumentQuery::from_cbor(
4845 where_cbor.as_slice(),
4846 &contract,
4847 document_type,
4848 &DriveConfig::default(),
4849 PlatformVersion::latest(),
4850 )
4851 .expect_err("starts with can not start with an empty string");
4852 }
4853
4854 #[test]
4855 fn test_invalid_query_limit_zero() {
4856 let query_value = json!({
4857 "where": [
4858 ["firstName", "startsWith", "a"],
4859 ],
4860 "limit": 0,
4861 "orderBy": [
4862 ["firstName", "asc"],
4863 ],
4864 });
4865
4866 let contract = get_data_contract_fixture(None, 0, 1).data_contract_owned();
4867 let document_type = contract
4868 .document_type_for_name("niceDocument")
4869 .expect("expected to get nice document");
4870
4871 let where_cbor = cbor_serializer::serializable_value_to_cbor(&query_value, None)
4872 .expect("expected to serialize to cbor");
4873 DriveDocumentQuery::from_cbor(
4874 where_cbor.as_slice(),
4875 &contract,
4876 document_type,
4877 &DriveConfig::default(),
4878 PlatformVersion::latest(),
4879 )
4880 .expect_err("starts with can not start with an empty string");
4881 }
4882
4883 #[test]
4884 fn resolved_time_range_shape_guard_accepts_only_the_single_resolution_equality() {
4885 use crate::query::{validate_resolved_time_range_clause_shapes, ResolvedTimeRange};
4886 use dpp::data_contract::document_type::TimeRangeTransform;
4887
4888 let resolved = vec![ResolvedTimeRange {
4889 transform: TimeRangeTransform {
4890 source: "$createdAt".to_string(),
4891 range_seconds: 21_600,
4892 step_seconds: 7_200,
4893 phase_seconds: 0,
4894 ttl_seconds: None,
4895 }
4896 .into(),
4897 }];
4898 let equality = WhereClause {
4899 field: "$createdAt".to_string(),
4900 operator: WhereOperator::Equal,
4901 value: Value::U64(21_600_000),
4902 };
4903 let other = WhereClause {
4904 field: "hashtag".to_string(),
4905 operator: WhereOperator::Equal,
4906 value: Value::Text("ibiza".to_string()),
4907 };
4908
4909 validate_resolved_time_range_clause_shapes(&[equality.clone(), other.clone()], &resolved)
4910 .expect("one equality on the resolved field is the resolution shape");
4911
4912 let in_clause = WhereClause {
4915 field: "$createdAt".to_string(),
4916 operator: WhereOperator::In,
4917 value: Value::Array(vec![Value::U64(0), Value::U64(7_200_000)]),
4918 };
4919 validate_resolved_time_range_clause_shapes(&[in_clause, other.clone()], &resolved)
4920 .expect_err("an In clause on a resolved field must be rejected");
4921
4922 let range_clause = WhereClause {
4923 field: "$createdAt".to_string(),
4924 operator: WhereOperator::GreaterThan,
4925 value: Value::U64(0),
4926 };
4927 validate_resolved_time_range_clause_shapes(&[equality.clone(), range_clause], &resolved)
4928 .expect_err("a range clause riding along on a resolved field must be rejected");
4929
4930 validate_resolved_time_range_clause_shapes(&[other], &resolved)
4931 .expect_err("a resolved field with no equality at all must be rejected");
4932 }
4933
4934 #[test]
4940 fn by_start_rejects_windows_past_the_ttl_horizon() {
4941 use crate::query::{resolve_time_range_bucket_clause, TimeRangeSelector};
4942 use dpp::data_contract::DataContractFactory;
4943 use dpp::platform_value::platform_value;
4944 use dpp::prelude::Identifier;
4945
4946 let factory =
4947 DataContractFactory::new(PlatformVersion::latest().protocol_version).expect("factory");
4948 let hour_ms: u64 = 3_600_000;
4949 let build = |seed: u8, with_ttl: bool| {
4950 let mut time_range = vec![
4951 (
4952 Value::Text("on".to_string()),
4953 Value::Text("$createdAt".to_string()),
4954 ),
4955 (Value::Text("range".to_string()), Value::U64(7_200)),
4956 (Value::Text("step".to_string()), Value::U64(7_200)),
4957 ];
4958 if with_ttl {
4959 time_range.push((Value::Text("ttl".to_string()), Value::U64(14_400)));
4960 }
4961 let index_map = vec![
4962 (
4963 Value::Text("name".to_string()),
4964 Value::Text("trending".to_string()),
4965 ),
4966 (
4967 Value::Text("properties".to_string()),
4968 Value::Array(vec![
4969 platform_value!({"$createdAt": "asc"}),
4970 platform_value!({"hashtag": "asc"}),
4971 ]),
4972 ),
4973 (Value::Text("timeRange".to_string()), Value::Map(time_range)),
4974 (
4975 Value::Text("countable".to_string()),
4976 Value::Text("countable".to_string()),
4977 ),
4978 ];
4979 let document_schema = platform_value!({
4980 "type": "object",
4981 "properties": {
4982 "hashtag": {"type": "string", "maxLength": 61, "position": 0},
4983 },
4984 "required": ["hashtag", "$createdAt"],
4985 "indices": Value::Array(vec![Value::Map(index_map)]),
4986 "additionalProperties": false,
4987 });
4988 factory
4989 .create_with_value_config(
4990 Identifier::from([seed; 32]),
4991 0,
4992 platform_value!({ "post": document_schema }),
4993 None,
4994 None,
4995 )
4996 .expect("contract registers")
4997 .data_contract_owned()
4998 };
4999
5000 let ttl_contract = build(101, true);
5001 let standing_contract = build(102, false);
5002 let expired_start = 5_000 * hour_ms;
5003 let block_time = expired_start + 6 * hour_ms;
5004
5005 let resolve = |contract: &DataContract, start_ms: u64| {
5006 resolve_time_range_bucket_clause(
5007 "$createdAt",
5008 TimeRangeSelector::ByStart { start_ms },
5009 None,
5010 contract
5011 .document_type_for_name("post")
5012 .expect("document type"),
5013 block_time,
5014 )
5015 };
5016
5017 let error = resolve(&ttl_contract, expired_start)
5018 .expect_err("a window past the ttl horizon must be rejected, not served");
5019 assert!(
5020 error.to_string().contains("ttl horizon"),
5021 "the rejection names the horizon: {error}"
5022 );
5023 resolve(&ttl_contract, expired_start + 2 * hour_ms).expect(
5024 "a window starting exactly at the horizon is not expired — same \
5025 strictly-below boundary the drain uses",
5026 );
5027 resolve(&ttl_contract, expired_start + 4 * hour_ms)
5028 .expect("a live window resolves normally");
5029 resolve_time_range_bucket_clause(
5030 "$createdAt",
5031 TimeRangeSelector::Newest,
5032 None,
5033 ttl_contract
5034 .document_type_for_name("post")
5035 .expect("document type"),
5036 block_time,
5037 )
5038 .expect("relative selectors never address expired windows and stay unaffected");
5039 resolve(&standing_contract, expired_start)
5040 .expect("without a ttl, arbitrarily old windows stay queryable");
5041 }
5042
5043 #[test]
5044 fn test_withdrawal_query_with_missing_transaction_index() {
5045 let (_, contract) = setup_withdrawal_contract();
5047 let platform_version = PlatformVersion::latest();
5048
5049 let document_type_name = "withdrawal";
5050 let document_type = contract
5051 .document_type_for_name(document_type_name)
5052 .expect("expected to get document type");
5053
5054 let drive_document_query = DriveDocumentQuery {
5056 contract: &contract,
5057 document_type,
5058 internal_clauses: InternalClauses {
5059 primary_key_in_clause: None,
5060 primary_key_equal_clause: None,
5061 in_clauses: vec![WhereClause {
5062 field: "status".to_string(),
5063 operator: WhereOperator::In,
5064 value: Value::Array(vec![
5065 Value::U64(0),
5066 Value::U64(1),
5067 Value::U64(2),
5068 Value::U64(3),
5069 Value::U64(4),
5070 ]),
5071 }],
5072 range_clause: None,
5073 equal_clauses: BTreeMap::default(),
5074 },
5075 offset: None,
5076 limit: Some(3),
5077 order_by: IndexMap::from([
5078 (
5079 "status".to_string(),
5080 OrderClause {
5081 field: "status".to_string(),
5082 ascending: true,
5083 },
5084 ),
5085 (
5086 "transactionIndex".to_string(),
5087 OrderClause {
5088 field: "transactionIndex".to_string(),
5089 ascending: true,
5090 },
5091 ),
5092 ]),
5093 start_at: Some([3u8; 32]),
5094 start_at_included: false,
5095 block_time_ms: None,
5096 resolved_time_ranges: vec![],
5097 sub_queries: vec![],
5098 };
5099
5100 let mut properties = BTreeMap::new();
5102 properties.insert("status".to_string(), Value::U64(0));
5103 let starts_at_document = DocumentV0 {
5106 contract_version: None,
5107 id: Identifier::from([3u8; 32]), owner_id: Identifier::random(),
5109 properties,
5110 revision: None,
5111 created_at: None,
5112 updated_at: None,
5113 transferred_at: None,
5114 created_at_block_height: None,
5115 updated_at_block_height: None,
5116 transferred_at_block_height: None,
5117 created_at_core_block_height: None,
5118 updated_at_core_block_height: None,
5119 transferred_at_core_block_height: None,
5120 creator_id: None,
5121 moderated_at: None,
5122 moderated_by: None,
5123 }
5124 .into();
5125
5126 let result = drive_document_query
5128 .construct_path_query(Some(starts_at_document), platform_version)
5129 .expect("expected to construct a path query");
5130
5131 assert_eq!(
5132 result
5133 .clone()
5134 .query
5135 .query
5136 .default_subquery_branch
5137 .subquery
5138 .expect("expected subquery")
5139 .items,
5140 Query::new_range_full().items
5141 );
5142 }
5143
5144 mod multiple_in_clause_lowering {
5151 use super::*;
5152 use crate::error::query::QuerySyntaxError;
5153 use crate::error::Error;
5154
5155 fn family_contract() -> DataContract {
5156 json_document_to_contract(
5157 "tests/supporting_files/contract/family/family-contract.json",
5158 false,
5159 PlatformVersion::latest(),
5160 )
5161 .expect("expected to load family contract")
5162 }
5163
5164 fn text_array(values: &[&str]) -> Value {
5165 Value::Array(
5166 values
5167 .iter()
5168 .map(|value| Value::Text(value.to_string()))
5169 .collect(),
5170 )
5171 }
5172
5173 fn in_clause(field: &str, values: &[&str]) -> WhereClause {
5174 WhereClause {
5175 field: field.to_string(),
5176 operator: WhereOperator::In,
5177 value: text_array(values),
5178 }
5179 }
5180
5181 fn ascending_order_by(fields: &[&str]) -> IndexMap<String, OrderClause> {
5182 fields
5183 .iter()
5184 .map(|field| {
5185 (
5186 field.to_string(),
5187 OrderClause {
5188 field: field.to_string(),
5189 ascending: true,
5190 },
5191 )
5192 })
5193 .collect()
5194 }
5195
5196 fn person_query<'a>(
5197 contract: &'a DataContract,
5198 where_clauses: Vec<WhereClause>,
5199 order_by_fields: &[&str],
5200 ) -> DriveDocumentQuery<'a> {
5201 let internal_clauses =
5202 InternalClauses::extract_from_clauses(where_clauses, PlatformVersion::latest())
5203 .expect("clauses should group structurally");
5204 DriveDocumentQuery {
5205 contract,
5206 document_type: contract
5207 .document_type_for_name("person")
5208 .expect("person document type should exist"),
5209 internal_clauses,
5210 offset: None,
5211 limit: Some(100),
5212 order_by: ascending_order_by(order_by_fields),
5213 start_at: None,
5214 start_at_included: false,
5215 block_time_ms: None,
5216 resolved_time_ranges: vec![],
5217 sub_queries: vec![],
5218 }
5219 }
5220
5221 #[test]
5222 fn two_in_clauses_lower_to_nested_key_sets() {
5223 let contract = family_contract();
5224 let platform_version = PlatformVersion::latest();
5225 let query = person_query(
5226 &contract,
5227 vec![
5228 in_clause("firstName", &["Adey", "Briney"]),
5229 in_clause("lastName", &["Kriskov", "Randolf"]),
5230 ],
5231 &["firstName", "lastName"],
5232 );
5233
5234 let path_query = query
5235 .construct_path_query(None, platform_version)
5236 .expect("two in clauses should lower at protocol version 14");
5237
5238 assert_eq!(
5241 path_query.path.last().expect("path should not be empty"),
5242 &b"firstName".to_vec()
5243 );
5244
5245 let outer = &path_query.query.query;
5247 assert_eq!(outer.items.len(), 2);
5248 assert!(outer.left_to_right);
5249
5250 assert_eq!(
5253 outer.default_subquery_branch.subquery_path,
5254 Some(vec![b"lastName".to_vec()])
5255 );
5256 let inner = outer
5257 .default_subquery_branch
5258 .subquery
5259 .as_deref()
5260 .expect("expected a lastName subquery");
5261 assert_eq!(inner.items.len(), 2);
5262
5263 assert_eq!(
5265 inner.default_subquery_branch.subquery_path,
5266 Some(vec![vec![0]])
5267 );
5268 }
5269
5270 #[test]
5271 #[cfg(feature = "cbor_query")]
5272 fn two_in_clauses_survive_cbor_round_trip() {
5273 let contract = family_contract();
5274 let mut query = person_query(
5275 &contract,
5276 vec![
5277 in_clause("firstName", &["Adey", "Briney"]),
5278 in_clause("lastName", &["Kriskov", "Randolf"]),
5279 ],
5280 &["firstName", "lastName"],
5281 );
5282 query.start_at_included = true;
5285
5286 let cbor = query.to_cbor().expect("should serialize cbor");
5287 let deserialized = DriveDocumentQuery::from_cbor(
5288 &cbor,
5289 &contract,
5290 contract
5291 .document_type_for_name("person")
5292 .expect("person document type should exist"),
5293 &DriveConfig::default(),
5294 PlatformVersion::latest(),
5295 )
5296 .expect("should deserialize cbor");
5297
5298 assert_eq!(query, deserialized);
5299 assert_eq!(
5300 deserialized
5301 .internal_clauses
5302 .in_clauses
5303 .iter()
5304 .map(|in_clause| in_clause.field.as_str())
5305 .collect::<Vec<_>>(),
5306 vec!["firstName", "lastName"],
5307 "both in clauses must survive the round trip in order"
5308 );
5309 }
5310
5311 #[test]
5312 fn descending_order_by_on_left_over_property_is_honored() {
5313 let contract = family_contract();
5314 let platform_version = PlatformVersion::latest();
5315 let mut query = person_query(
5318 &contract,
5319 vec![
5320 in_clause("firstName", &["Adey", "Briney"]),
5321 in_clause("middleName", &["Ivanna", "Evangeline"]),
5322 ],
5323 &["firstName", "middleName"],
5324 );
5325 query.order_by.insert(
5326 "lastName".to_string(),
5327 OrderClause {
5328 field: "lastName".to_string(),
5329 ascending: false,
5330 },
5331 );
5332
5333 let path_query = query
5334 .construct_path_query(None, platform_version)
5335 .expect("two in clauses with a left-over order should lower");
5336
5337 let outer = &path_query.query.query;
5338 let middle = outer
5339 .default_subquery_branch
5340 .subquery
5341 .as_deref()
5342 .expect("expected a middleName subquery");
5343 assert_eq!(
5344 middle.default_subquery_branch.subquery_path,
5345 Some(vec![b"lastName".to_vec()])
5346 );
5347 let left_over_level = middle
5348 .default_subquery_branch
5349 .subquery
5350 .as_deref()
5351 .expect("expected a lastName subquery");
5352 assert!(
5353 !left_over_level.left_to_right,
5354 "left-over lastName level must honor the descending order by"
5355 );
5356
5357 query.order_by.shift_remove("lastName");
5360 let path_query = query
5361 .construct_path_query(None, platform_version)
5362 .expect("two in clauses should lower");
5363 let left_over_level = path_query
5364 .query
5365 .query
5366 .default_subquery_branch
5367 .subquery
5368 .as_deref()
5369 .expect("expected a middleName subquery")
5370 .default_subquery_branch
5371 .subquery
5372 .as_deref()
5373 .expect("expected a lastName subquery");
5374 assert!(left_over_level.left_to_right);
5375 }
5376
5377 #[test]
5378 fn two_in_clauses_rejected_at_protocol_version_13() {
5379 let contract = family_contract();
5380 let platform_version_13 =
5381 PlatformVersion::get(13).expect("protocol version 13 should exist");
5382 let query = person_query(
5383 &contract,
5384 vec![
5385 in_clause("firstName", &["Adey", "Briney"]),
5386 in_clause("lastName", &["Kriskov", "Randolf"]),
5387 ],
5388 &["firstName", "lastName"],
5389 );
5390
5391 let error = query
5392 .construct_path_query(None, platform_version_13)
5393 .expect_err("multiple in clauses must be rejected before protocol version 14");
5394 assert!(
5395 matches!(error, Error::Query(QuerySyntaxError::MultipleInClauses(_))),
5396 "expected MultipleInClauses, got {error:?}"
5397 );
5398
5399 query
5400 .construct_path_query(None, PlatformVersion::latest())
5401 .expect("the same query should lower at protocol version 14");
5402 }
5403
5404 #[test]
5405 fn equality_prefix_two_in_clauses_and_trailing_range_lowering() {
5406 let contract = family_contract();
5407 let platform_version = PlatformVersion::latest();
5408 let mut query = person_query(
5409 &contract,
5410 vec![
5411 WhereClause {
5412 field: "age".to_string(),
5413 operator: WhereOperator::Equal,
5414 value: Value::U8(30),
5415 },
5416 in_clause("firstName", &["Adey", "Briney"]),
5417 in_clause("middleName", &["Ivanna", "Evangeline"]),
5418 WhereClause {
5419 field: "lastName".to_string(),
5420 operator: WhereOperator::GreaterThan,
5421 value: Value::Text("M".to_string()),
5422 },
5423 ],
5424 &["firstName", "middleName", "lastName"],
5425 );
5426 query.limit = Some(50);
5427
5428 let path_query = query
5432 .construct_path_query(None, platform_version)
5433 .expect("equality + in + in + range should lower");
5434
5435 let path_len = path_query.path.len();
5436 assert_eq!(path_query.path[path_len - 3], b"age".to_vec());
5437 assert_eq!(
5438 path_query.path.last().expect("path should not be empty"),
5439 &b"firstName".to_vec()
5440 );
5441
5442 let outer = &path_query.query.query;
5443 assert_eq!(outer.items.len(), 2);
5444 assert_eq!(
5445 outer.default_subquery_branch.subquery_path,
5446 Some(vec![b"middleName".to_vec()])
5447 );
5448 let middle = outer
5449 .default_subquery_branch
5450 .subquery
5451 .as_deref()
5452 .expect("expected a middleName subquery");
5453 assert_eq!(middle.items.len(), 2);
5454 assert_eq!(
5455 middle.default_subquery_branch.subquery_path,
5456 Some(vec![b"lastName".to_vec()])
5457 );
5458 let range_level = middle
5459 .default_subquery_branch
5460 .subquery
5461 .as_deref()
5462 .expect("expected a lastName subquery");
5463 assert_eq!(range_level.items.len(), 1);
5465 assert_eq!(
5466 range_level.default_subquery_branch.subquery_path,
5467 Some(vec![vec![0]])
5468 );
5469 }
5470
5471 #[test]
5472 fn cross_product_above_cap_is_rejected() {
5473 let contract = family_contract();
5474 let first_names: Vec<String> = (0..20).map(|i| format!("First{i:02}")).collect();
5475 let last_names: Vec<String> = (0..6).map(|i| format!("Last{i}")).collect();
5476 let query = person_query(
5477 &contract,
5478 vec![
5479 WhereClause {
5480 field: "firstName".to_string(),
5481 operator: WhereOperator::In,
5482 value: Value::Array(first_names.iter().cloned().map(Value::Text).collect()),
5483 },
5484 WhereClause {
5485 field: "lastName".to_string(),
5486 operator: WhereOperator::In,
5487 value: Value::Array(last_names.iter().cloned().map(Value::Text).collect()),
5488 },
5489 ],
5490 &["firstName", "lastName"],
5491 );
5492
5493 let error = query
5494 .construct_path_query(None, PlatformVersion::latest())
5495 .expect_err("a 120-branch cross product must be rejected");
5496 assert!(
5497 matches!(error, Error::Query(QuerySyntaxError::InvalidInClause(_))),
5498 "expected InvalidInClause, got {error:?}"
5499 );
5500 }
5501
5502 #[test]
5503 fn non_consecutive_in_fields_are_rejected() {
5504 let contract = family_contract();
5505 let query = person_query(
5509 &contract,
5510 vec![
5511 in_clause("middleName", &["Ivanna", "Evangeline"]),
5512 in_clause("lastName", &["Kriskov", "Randolf"]),
5513 ],
5514 &["middleName", "lastName"],
5515 );
5516
5517 let error = query
5518 .construct_path_query(None, PlatformVersion::latest())
5519 .expect_err("non-consecutive in clauses must be rejected");
5520 assert!(
5521 matches!(
5522 error,
5523 Error::Query(QuerySyntaxError::WhereClauseOnNonIndexedProperty(_))
5524 ),
5525 "expected WhereClauseOnNonIndexedProperty, got {error:?}"
5526 );
5527 }
5528
5529 #[test]
5530 fn cursor_pagination_is_rejected() {
5531 let contract = family_contract();
5532 let mut query = person_query(
5533 &contract,
5534 vec![
5535 in_clause("firstName", &["Adey", "Briney"]),
5536 in_clause("lastName", &["Kriskov", "Randolf"]),
5537 ],
5538 &["firstName", "lastName"],
5539 );
5540 query.start_at = Some([5u8; 32]);
5541 query.start_at_included = false;
5542
5543 let error = query
5544 .construct_path_query(None, PlatformVersion::latest())
5545 .expect_err("cursor pagination with multiple in clauses must be rejected");
5546 assert!(
5547 matches!(error, Error::Query(QuerySyntaxError::Unsupported(_))),
5548 "expected Unsupported, got {error:?}"
5549 );
5550 }
5551
5552 #[test]
5553 fn missing_order_by_on_an_in_field_is_rejected() {
5554 let contract = family_contract();
5555 let query = person_query(
5556 &contract,
5557 vec![
5558 in_clause("firstName", &["Adey", "Briney"]),
5559 in_clause("lastName", &["Kriskov", "Randolf"]),
5560 ],
5561 &["firstName"],
5562 );
5563
5564 let error = query
5565 .construct_path_query(None, PlatformVersion::latest())
5566 .expect_err("missing order by on an in field must be rejected");
5567 assert!(
5572 matches!(
5573 error,
5574 Error::Query(QuerySyntaxError::WhereClauseOnNonIndexedProperty(_))
5575 ),
5576 "expected WhereClauseOnNonIndexedProperty, got {error:?}"
5577 );
5578 }
5579 }
5580
5581 mod summable_start_after_page {
5587 use super::*;
5588 use crate::error::drive::DriveError;
5589 use crate::error::Error;
5590 use crate::util::object_size_info::{
5591 DocumentAndContractInfo, DocumentInfo, OwnedDocumentInfo,
5592 };
5593 use dpp::data_contract::document_type::random_document::CreateRandomDocument;
5594 use dpp::document::serialization_traits::DocumentPlatformConversionMethodsV0;
5595 use dpp::document::{Document, DocumentV0Getters, DocumentV0Setters};
5596 use grovedb::query_result_type::QueryResultType;
5597 use grovedb::Element;
5598
5599 const TIP_COUNT: u64 = 5;
5600
5601 fn setup_tip_jar() -> (Drive, DataContract) {
5602 let platform_version = PlatformVersion::latest();
5603 let drive = setup_drive_with_initial_state_structure(None);
5604 let contract = json_document_to_contract(
5605 "tests/supporting_files/contract/tip-jar/tip-jar-contract.json",
5606 false,
5607 platform_version,
5608 )
5609 .expect("expected to load the tip jar contract");
5610 drive
5611 .apply_contract(
5612 &contract,
5613 BlockInfo::default(),
5614 true,
5615 None,
5616 None,
5617 platform_version,
5618 )
5619 .expect("expected to apply the tip jar contract");
5620 (drive, contract)
5621 }
5622
5623 fn setup_tips(drive: &Drive, contract: &DataContract) -> Vec<Identifier> {
5626 let platform_version = PlatformVersion::latest();
5627 let document_type = contract
5628 .document_type_for_name("tip")
5629 .expect("tip type should exist");
5630 (1..=TIP_COUNT)
5631 .map(|sent_at| {
5632 let mut tip = document_type
5633 .random_document(Some(sent_at), platform_version)
5634 .expect("expected a random tip");
5635 tip.set("recipient", Value::Identifier([7; 32]));
5636 tip.set("amount", Value::U64(sent_at * 10));
5637 tip.set("sentAt", Value::U64(sent_at));
5638 drive
5639 .add_document_for_contract(
5640 DocumentAndContractInfo {
5641 owned_document_info: OwnedDocumentInfo {
5642 document_info: DocumentInfo::DocumentRefInfo((&tip, None)),
5643 owner_id: None,
5644 },
5645 contract,
5646 document_type,
5647 },
5648 false,
5649 BlockInfo::default(),
5650 true,
5651 None,
5652 platform_version,
5653 None,
5654 )
5655 .expect("expected to insert a tip");
5656 tip.id()
5657 })
5658 .collect()
5659 }
5660
5661 fn tips_after<'a>(
5662 contract: &'a DataContract,
5663 cursor: Identifier,
5664 offset: Option<u16>,
5665 limit: u16,
5666 ) -> DriveDocumentQuery<'a> {
5667 let internal_clauses = InternalClauses::extract_from_clauses(
5668 vec![WhereClause {
5669 field: "sentAt".to_string(),
5670 operator: WhereOperator::GreaterThan,
5671 value: Value::U64(0),
5672 }],
5673 PlatformVersion::latest(),
5674 )
5675 .expect("clauses should group structurally");
5676 DriveDocumentQuery {
5677 contract,
5678 document_type: contract
5679 .document_type_for_name("tip")
5680 .expect("tip type should exist"),
5681 internal_clauses,
5682 offset,
5683 limit: Some(limit),
5684 order_by: IndexMap::from([(
5685 "sentAt".to_string(),
5686 OrderClause {
5687 field: "sentAt".to_string(),
5688 ascending: true,
5689 },
5690 )]),
5691 start_at: Some(cursor.to_buffer()),
5692 start_at_included: false,
5693 block_time_ms: None,
5694 resolved_time_ranges: vec![],
5695 sub_queries: vec![],
5696 }
5697 }
5698
5699 fn ids(serialized_documents: &[Vec<u8>], contract: &DataContract) -> Vec<Identifier> {
5700 let document_type = contract
5701 .document_type_for_name("tip")
5702 .expect("tip type should exist");
5703 serialized_documents
5704 .iter()
5705 .map(|bytes| {
5706 Document::from_bytes(bytes, document_type, PlatformVersion::latest())
5707 .expect("expected a tip")
5708 .id()
5709 })
5710 .collect()
5711 }
5712
5713 #[test]
5714 fn should_drop_the_start_after_cursor_from_an_element_page_of_a_summable_type() {
5715 let platform_version = PlatformVersion::latest();
5716 let (drive, contract) = setup_tip_jar();
5717 let tip_ids = setup_tips(&drive, &contract);
5718
5719 for (position, cursor) in tip_ids.iter().enumerate() {
5720 for (offset, limit) in [(None, 1u16), (None, 2), (Some(1u16), 2)] {
5721 let context = format!("cursor {position}, offset {offset:?}, limit {limit}");
5722 let expected: Vec<Identifier> = tip_ids[position + 1..]
5723 .iter()
5724 .skip(offset.unwrap_or(0) as usize)
5725 .take(limit as usize)
5726 .copied()
5727 .collect();
5728 let query = tips_after(&contract, *cursor, offset, limit);
5729 assert!(query.pads_cursor_page(platform_version), "{context}");
5730
5731 let (elements, element_skipped) = query
5732 .execute_no_proof_internal(
5733 &drive,
5734 QueryResultType::QueryElementResultType,
5735 None,
5736 &mut vec![],
5737 platform_version,
5738 )
5739 .expect("expected the element page");
5740 let elements = elements.to_elements();
5741 assert!(
5742 elements
5743 .iter()
5744 .all(|element| matches!(element, Element::ItemWithSumItem(..))),
5745 "{context}: a summable type's documents are items with a sum"
5746 );
5747 let element_page: Vec<Vec<u8>> = elements
5748 .iter()
5749 .map(|element| element.as_item_bytes().expect("expected an item").to_vec())
5750 .collect();
5751 let element_page = ids(&element_page, &contract);
5752 assert!(
5753 !element_page.contains(cursor),
5754 "{context}: the cursor document is on the element page"
5755 );
5756 assert_eq!(element_page, expected, "{context}: element page");
5757
5758 let (serialized, serialized_skipped, _) = query
5759 .execute_raw_results_no_proof(&drive, None, None, platform_version)
5760 .expect("expected the serialized page");
5761 assert_eq!(
5762 ids(&serialized, &contract),
5763 element_page,
5764 "{context}: serialized page"
5765 );
5766 assert_eq!(element_skipped, serialized_skipped, "{context}: skipped");
5767
5768 let (_, proved, _) = query
5769 .execute_with_proof_only_get_elements(&drive, None, None, platform_version)
5770 .expect("expected the proved page");
5771 assert_eq!(
5772 ids(&proved, &contract),
5773 element_page,
5774 "{context}: proved page"
5775 );
5776 }
5777 }
5778 }
5779
5780 #[test]
5784 fn should_refuse_a_summable_cursor_with_the_released_cursor_read() {
5785 let (drive, contract) = setup_tip_jar();
5786 let tip_ids = setup_tips(&drive, &contract);
5787 let mut released_cursor_read = PlatformVersion::latest().clone();
5788 released_cursor_read
5789 .drive
5790 .methods
5791 .document
5792 .query
5793 .start_at_document = 0;
5794 let query = tips_after(&contract, tip_ids[1], None, 2);
5795 let refused = |result: Result<(), Error>| {
5796 matches!(
5797 result,
5798 Err(Error::Drive(DriveError::CorruptedDocumentPath(
5799 "Holding paths should only have items"
5800 )))
5801 )
5802 };
5803 assert!(refused(
5804 query
5805 .execute_no_proof_internal(
5806 &drive,
5807 QueryResultType::QueryElementResultType,
5808 None,
5809 &mut vec![],
5810 &released_cursor_read,
5811 )
5812 .map(|_| ())
5813 ));
5814 assert!(refused(
5815 query
5816 .execute_raw_results_no_proof(&drive, None, None, &released_cursor_read)
5817 .map(|_| ())
5818 ));
5819 assert!(refused(
5820 query
5821 .execute_with_proof_only_get_elements(&drive, None, None, &released_cursor_read)
5822 .map(|_| ())
5823 ));
5824 }
5825 }
5826}