1#[cfg(feature = "server")]
2use dpp::data_contract::document_type::DocumentPropertyType;
3use dpp::data_contract::document_type::{
4 IndexBucketing, IntegerRangeTransform, TimeRangeTransform,
5};
6use std::sync::Arc;
7
8#[cfg(any(feature = "server", feature = "verify"))]
9pub use {
10 chained_document_query::{
14 ChainedDocumentsResult, ChainedOuterDocuments, MAX_CHAINED_JOIN_VALUES,
15 },
16 composite_document_query::{
20 BindingSource, CompositeDocumentsResult, DriveSubQuery, SubQueryBinding, SubQueryKind,
21 SubQueryResult, MAX_BOUND_VALUES, MAX_SUB_QUERIES,
22 },
23 conditions::{ValueClause, WhereClause, WhereOperator},
24 drive_document_average_query::{AverageEntry, AverageMode},
29 drive_document_count_query::{
36 CountMode, DocumentCountMode, DriveDocumentCountQuery, SplitCountEntry,
37 },
38 drive_document_having_query::{
44 AxisRangeBounds, DocumentHavingMode, DriveDocumentHavingQuery, MAX_HAVING_LIMIT,
45 },
46 drive_document_ranked_query::{
52 DocumentRankedMode, DriveDocumentRankedQuery, RankedAxis, RankedEntry, RankedEntryValue,
53 RankedPage, RankedPaginationInputs, MAX_RANKED_LIMIT, RANKED_AVG_SCALE,
54 RANKED_COUNT_ORDER_KEY,
55 },
56 drive_document_sum_query::{DriveDocumentSumQuery, SumEntry, SumMode},
61 grovedb::{PathQuery, Query, QueryItem, SizedQuery},
62 having::{
63 HavingAggregate, HavingAggregateFunction, HavingClause, HavingOperator, HavingRightOperand,
64 },
65 ordering::OrderClause,
66 projection::{SelectFunction, SelectProjection},
67 single_document_drive_query::SingleDocumentDriveQuery,
68 single_document_drive_query::SingleDocumentDriveQueryContestedStatus,
69 vote_polls_by_end_date_query::VotePollsByEndDateDriveQuery,
70 vote_query::IdentityBasedVoteDriveQuery,
71};
72
73#[cfg(feature = "server")]
76pub use drive_document_count_query::{
77 DocumentCountRequest, DocumentCountResponse, RangeCountOptions, MAX_LIMIT_AS_FAILSAFE,
78};
79
80#[cfg(feature = "server")]
84pub use drive_document_sum_query::{
85 DocumentSumRequest, DocumentSumResponse, RangeSumOptions, RangeSumWalkMode,
86};
87
88#[cfg(feature = "server")]
92pub use drive_document_average_query::{DocumentAverageRequest, DocumentAverageResponse};
93
94#[cfg(feature = "server")]
99pub use drive_document_ranked_query::{DocumentRankedRequest, DocumentRankedResponse};
100
101#[cfg(feature = "server")]
106pub use drive_document_having_query::{DocumentHavingRequest, DocumentHavingResponse};
107#[cfg(any(feature = "server", feature = "verify"))]
109use {
110 crate::{
111 drive::contract::paths::DataContractPaths,
112 error::{drive::DriveError, query::QuerySyntaxError, Error},
113 },
114 dpp::{
115 data_contract::{
116 accessors::v0::DataContractV0Getters,
117 document_type::{accessors::DocumentTypeV0Getters, methods::DocumentTypeV0Methods},
118 document_type::{DocumentTypeRef, Index},
119 DataContract,
120 },
121 document::{document_methods::DocumentMethodsV0, Document},
122 platform_value::{btreemap_extensions::BTreeValueRemoveFromMapHelper, Value},
123 version::PlatformVersion,
124 ProtocolError,
125 },
126 indexmap::IndexMap,
127 sqlparser::{
128 ast::{self, OrderByExpr, Select, Statement, TableFactor::Table, Value::Number},
129 dialect::MySqlDialect,
130 parser::Parser,
131 },
132 std::{collections::BTreeMap, ops::BitXor},
133};
134
135#[cfg(all(feature = "server", feature = "verify"))]
136use crate::verify::RootHash;
137
138#[cfg(any(feature = "server", feature = "verify"))]
139use crate::drive::document::ranked_index_tree_type::property_name_tree_type_and_ranked_axes_for_level;
140#[cfg(any(feature = "server", feature = "verify"))]
141use dpp::data_contract::document_type::accessors::DocumentTypeV2Getters;
142#[cfg(feature = "server")]
143use dpp::document::serialization_traits::DocumentPlatformConversionMethodsV0;
144#[cfg(any(feature = "server", feature = "verify"))]
145use dpp::document::DocumentV0Getters;
146#[cfg(feature = "server")]
147pub use grovedb::{
148 query_result_type::{QueryResultElements, QueryResultType},
149 Element, Error as GroveError, TransactionArg,
150};
151
152use dpp::document;
153use dpp::prelude::Identifier;
154use dpp::validation::{SimpleValidationResult, ValidationResult};
155#[cfg(feature = "server")]
156use {
157 crate::{drive::Drive, fees::op::LowLevelDriveOperation},
158 dpp::block::block_info::BlockInfo,
159};
160use crate::config::DriveConfig;
162use crate::util::common::encode::encode_u64;
164#[cfg(feature = "server")]
165use crate::util::grove_operations::QueryType::StatefulQuery;
166#[cfg(feature = "server")]
167use dpp::version::FeatureVersion;
168
169#[cfg(any(feature = "server", feature = "verify"))]
171pub mod canonicalize;
172#[cfg(any(feature = "server", feature = "verify"))]
173pub use canonicalize::validate_and_canonicalize_where_clauses;
174#[cfg(any(feature = "server", feature = "verify"))]
175pub mod conditions;
176#[cfg(any(feature = "server", feature = "verify"))]
177mod defaults;
178#[cfg(any(feature = "server", feature = "verify"))]
179pub mod having;
180mod non_primary_key_path_query;
181#[cfg(any(feature = "server", feature = "verify"))]
182pub mod ordering;
183#[cfg(any(feature = "server", feature = "verify"))]
184pub mod projection;
185#[cfg(any(feature = "server", feature = "verify"))]
186mod single_document_drive_query;
187pub(crate) mod where_clause_grouping;
189
190#[cfg(feature = "server")]
192mod test_index;
193
194#[cfg(any(feature = "server", feature = "verify"))]
195pub mod vote_poll_vote_state_query;
197#[cfg(any(feature = "server", feature = "verify"))]
198pub mod vote_query;
200
201#[cfg(any(feature = "server", feature = "verify"))]
202pub mod vote_poll_contestant_votes_query;
204
205#[cfg(any(feature = "server", feature = "verify"))]
206pub mod vote_polls_by_end_date_query;
208
209#[cfg(any(feature = "server", feature = "verify"))]
210pub mod vote_polls_by_document_type_query;
212
213#[cfg(any(feature = "server", feature = "verify"))]
219pub type ContractLookupFn<'a> =
220 dyn Fn(&Identifier) -> Result<Option<Arc<DataContract>>, Error> + 'a;
221
222#[cfg(any(feature = "server", feature = "verify"))]
233pub fn contract_lookup_fn_for_contract<'a>(
234 data_contract: Arc<DataContract>,
235) -> Box<ContractLookupFn<'a>> {
236 let func = move |id: &Identifier| -> Result<Option<Arc<DataContract>>, Error> {
237 if data_contract.id().ne(id) {
238 return Ok(None);
239 }
240 Ok(Some(Arc::clone(&data_contract)))
241 };
242 Box::new(func)
243}
244
245#[cfg(any(feature = "server", feature = "verify"))]
247pub mod contested_resource_votes_given_by_identity_query;
248#[cfg(any(feature = "server", feature = "verify"))]
250pub mod drive_contested_document_query;
251
252#[cfg(any(feature = "server", feature = "verify"))]
254pub mod proposer_block_count_query;
255
256#[cfg(any(feature = "server", feature = "verify"))]
258pub mod identity_token_balance_drive_query;
259#[cfg(any(feature = "server", feature = "verify"))]
261pub mod identity_token_info_drive_query;
262
263#[cfg(any(feature = "server", feature = "verify"))]
265pub mod filter;
266#[cfg(any(feature = "server", feature = "verify"))]
268pub mod token_status_drive_query;
269
270#[cfg(any(feature = "server", feature = "verify"))]
272pub mod drive_document_count_query;
273
274#[cfg(any(feature = "server", feature = "verify"))]
280pub mod drive_document_sum_query;
281
282#[cfg(any(feature = "server", feature = "verify"))]
289pub mod drive_document_average_query;
290
291#[cfg(any(feature = "server", feature = "verify"))]
297pub mod drive_document_having_query;
298
299#[cfg(any(feature = "server", feature = "verify"))]
306pub mod drive_document_ranked_query;
307
308#[cfg(any(feature = "server", feature = "verify"))]
313pub(crate) mod index_only_synthesis;
314
315#[cfg(any(feature = "server", feature = "verify"))]
321pub mod chained_document_query;
322
323#[cfg(any(feature = "server", feature = "verify"))]
327pub mod moderated_join;
328
329#[cfg(any(feature = "server", feature = "verify"))]
333pub mod composite_document_query;
334
335#[cfg(feature = "server")]
340pub mod drive_document_count_and_sum_query;
341
342pub type QuerySyntaxValidationResult<TData> = ValidationResult<TData, QuerySyntaxError>;
344
345pub type QuerySyntaxSimpleValidationResult = SimpleValidationResult<QuerySyntaxError>;
347
348#[cfg(any(feature = "server", feature = "verify"))]
349#[derive(Debug, Clone)]
356pub struct StartAtDocument<'a> {
357 pub document: Document,
359
360 pub document_type: DocumentTypeRef<'a>,
362
363 pub included: bool,
367}
368
369#[cfg(any(feature = "server", feature = "verify"))]
371#[derive(Clone, Debug, PartialEq, Default)]
372pub struct InternalClauses {
373 pub primary_key_in_clause: Option<WhereClause>,
375 pub primary_key_equal_clause: Option<WhereClause>,
377 pub in_clauses: Vec<WhereClause>,
384 pub range_clause: Option<WhereClause>,
391 pub equal_clauses: BTreeMap<String, WhereClause>,
393}
394
395#[cfg(any(feature = "server", feature = "verify"))]
401#[derive(Debug, Clone, Copy, Default, PartialEq, Eq)]
402pub struct ClauseFieldRoles {
403 pub primary_key: bool,
405 pub index_property: bool,
407 pub terminal: bool,
410}
411
412#[cfg(any(feature = "server", feature = "verify"))]
413impl ClauseFieldRoles {
414 pub fn unindexed(&self) -> bool {
417 !self.primary_key && !self.index_property && !self.terminal
418 }
419}
420
421#[cfg(any(feature = "server", feature = "verify"))]
428pub(crate) enum BestIndexOutcome<'a> {
429 Matched(&'a Index),
431 NoIndexMatches(Error),
434}
435
436impl InternalClauses {
437 #[cfg(any(feature = "server", feature = "verify"))]
441 pub fn skip_if_absent_bindings<'a>(
442 &'a self,
443 order_by_keys: &[&'a str],
444 ) -> Vec<SkipIfAbsentBinding<'a>> {
445 self.equal_clauses
446 .values()
447 .chain(self.in_clauses.iter())
448 .chain(self.range_clause.iter())
449 .map(SkipIfAbsentBinding::for_where_clause)
450 .chain(
451 order_by_keys
452 .iter()
453 .map(|key| SkipIfAbsentBinding::ordering(key)),
454 )
455 .collect()
456 }
457
458 #[cfg(any(feature = "server", feature = "verify"))]
464 pub fn classify_field(document_type: DocumentTypeRef, field: &str) -> ClauseFieldRoles {
465 let mut roles = ClauseFieldRoles {
466 primary_key: field == "$id",
467 ..Default::default()
468 };
469 for index in document_type.indexes().values() {
470 if index
471 .properties
472 .iter()
473 .any(|property| property.name == field)
474 {
475 roles.index_property = true;
476 }
477 if index.terminal_contains(field) {
478 roles.terminal = true;
479 }
480 if roles.index_property && roles.terminal {
481 break;
482 }
483 }
484 roles
485 }
486
487 #[cfg(any(feature = "server", feature = "verify"))]
491 pub fn classify_fields(
492 &self,
493 document_type: DocumentTypeRef,
494 ) -> BTreeMap<String, ClauseFieldRoles> {
495 let mut classified = BTreeMap::new();
496 let mut add = |field: &str| {
497 classified
498 .entry(field.to_string())
499 .or_insert_with(|| Self::classify_field(document_type, field));
500 };
501 if self.primary_key_equal_clause.is_some() || self.primary_key_in_clause.is_some() {
502 add("$id");
503 }
504 for field in self.equal_clauses.keys() {
505 add(field);
506 }
507 if let Some(range_clause) = &self.range_clause {
508 add(&range_clause.field);
509 }
510 for in_clause in &self.in_clauses {
511 add(&in_clause.field);
512 }
513 classified
514 }
515
516 #[cfg(any(feature = "server", feature = "verify"))]
517 pub fn verify(&self) -> bool {
519 if self
521 .primary_key_in_clause
522 .is_some()
523 .bitxor(self.primary_key_equal_clause.is_some())
524 {
525 !(!self.in_clauses.is_empty()
527 || self.range_clause.is_some()
528 || !self.equal_clauses.is_empty())
529 } else {
530 !(self.primary_key_in_clause.is_some() && self.primary_key_equal_clause.is_some())
531 }
532 }
533
534 #[cfg(any(feature = "server", feature = "verify"))]
535 pub fn is_for_primary_key(&self) -> bool {
537 self.primary_key_in_clause.is_some() || self.primary_key_equal_clause.is_some()
538 }
539
540 #[cfg(any(feature = "server", feature = "verify"))]
541 pub fn is_empty(&self) -> bool {
543 self.in_clauses.is_empty()
544 && self.range_clause.is_none()
545 && self.equal_clauses.is_empty()
546 && self.primary_key_in_clause.is_none()
547 && self.primary_key_equal_clause.is_none()
548 }
549
550 #[cfg(any(feature = "server", feature = "verify"))]
551 pub fn extract_from_clauses(
553 all_where_clauses: Vec<WhereClause>,
554 platform_version: &PlatformVersion,
555 ) -> Result<Self, Error> {
556 let primary_key_equal_clauses_array = all_where_clauses
557 .iter()
558 .filter_map(|where_clause| match where_clause.operator {
559 WhereOperator::Equal => match where_clause.is_identifier() {
560 true => Some(where_clause.clone()),
561 false => None,
562 },
563 _ => None,
564 })
565 .collect::<Vec<WhereClause>>();
566
567 let primary_key_in_clauses_array = all_where_clauses
568 .iter()
569 .filter_map(|where_clause| match where_clause.operator {
570 WhereOperator::In => match where_clause.is_identifier() {
571 true => Some(where_clause.clone()),
572 false => None,
573 },
574 _ => None,
575 })
576 .collect::<Vec<WhereClause>>();
577
578 let (equal_clauses, range_clause, in_clauses) =
579 WhereClause::group_clauses(&all_where_clauses, platform_version)?;
580
581 let primary_key_equal_clause = match primary_key_equal_clauses_array.len() {
582 0 => Ok(None),
583 1 => Ok(Some(
584 primary_key_equal_clauses_array
585 .first()
586 .expect("there must be a value")
587 .clone(),
588 )),
589 _ => Err(Error::Query(
590 QuerySyntaxError::DuplicateNonGroupableClauseSameField(
591 "There should only be one equal clause for the primary key",
592 ),
593 )),
594 }?;
595
596 let primary_key_in_clause = match primary_key_in_clauses_array.len() {
597 0 => Ok(None),
598 1 => Ok(Some(
599 primary_key_in_clauses_array
600 .first()
601 .expect("there must be a value")
602 .clone(),
603 )),
604 _ => Err(Error::Query(
605 QuerySyntaxError::DuplicateNonGroupableClauseSameField(
606 "There should only be one in clause for the primary key",
607 ),
608 )),
609 }?;
610
611 let internal_clauses = InternalClauses {
612 primary_key_equal_clause,
613 primary_key_in_clause,
614 in_clauses,
615 range_clause,
616 equal_clauses,
617 };
618
619 match internal_clauses.verify() {
620 true => Ok(internal_clauses),
621 false => Err(Error::Query(
622 QuerySyntaxError::InvalidWhereClauseComponents("Query has invalid where clauses"),
623 )),
624 }
625 }
626
627 #[cfg(any(feature = "server", feature = "verify"))]
629 pub fn validate_against_schema(
630 &self,
631 document_type: DocumentTypeRef,
632 ) -> QuerySyntaxSimpleValidationResult {
633 if !self.verify() {
635 return QuerySyntaxSimpleValidationResult::new_with_error(
636 QuerySyntaxError::InvalidWhereClauseComponents(
637 "invalid composition of where clauses",
638 ),
639 );
640 }
641
642 for in_clause in &self.in_clauses {
644 if in_clause.field == "$id" {
646 return QuerySyntaxSimpleValidationResult::new_with_error(
647 QuerySyntaxError::InvalidWhereClauseComponents(
648 "use primary_key_* clauses for $id",
649 ),
650 );
651 }
652 let result = in_clause.validate_against_schema(document_type);
653 if !result.is_valid() {
654 return result;
655 }
656 }
657
658 if let Some(range_clause) = &self.range_clause {
660 if range_clause.field == "$id" {
662 return QuerySyntaxSimpleValidationResult::new_with_error(
663 QuerySyntaxError::InvalidWhereClauseComponents(
664 "use primary_key_* clauses for $id",
665 ),
666 );
667 }
668 let result = range_clause.validate_against_schema(document_type);
669 if !result.is_valid() {
670 return result;
671 }
672 }
673
674 for (field, eq_clause) in &self.equal_clauses {
676 if field.as_str() == "$id" {
678 return QuerySyntaxSimpleValidationResult::new_with_error(
679 QuerySyntaxError::InvalidWhereClauseComponents(
680 "use primary_key_* clauses for $id",
681 ),
682 );
683 }
684 let result = eq_clause.validate_against_schema(document_type);
685 if !result.is_valid() {
686 return result;
687 }
688 }
689
690 if let Some(pk_eq) = &self.primary_key_equal_clause {
692 if pk_eq.operator != WhereOperator::Equal
693 || !matches!(pk_eq.value, Value::Identifier(_))
694 {
695 return QuerySyntaxSimpleValidationResult::new_with_error(
696 QuerySyntaxError::InvalidWhereClauseComponents(
697 "primary key equality must compare an identifier",
698 ),
699 );
700 }
701 }
702 if let Some(pk_in) = &self.primary_key_in_clause {
703 if pk_in.operator != WhereOperator::In {
704 return QuerySyntaxSimpleValidationResult::new_with_error(
705 QuerySyntaxError::InvalidWhereClauseComponents(
706 "primary key IN must use IN operator",
707 ),
708 );
709 }
710 let result = pk_in.in_values();
712 if !result.is_valid() {
713 return QuerySyntaxSimpleValidationResult::new_with_errors(result.errors);
714 }
715 if let Value::Array(arr) = &pk_in.value {
716 if !arr.iter().all(|v| matches!(v, Value::Identifier(_))) {
717 return QuerySyntaxSimpleValidationResult::new_with_error(
718 QuerySyntaxError::InvalidWhereClauseComponents(
719 "primary key IN must contain identifiers",
720 ),
721 );
722 }
723 } else {
724 return QuerySyntaxSimpleValidationResult::new_with_error(
725 QuerySyntaxError::InvalidWhereClauseComponents(
726 "primary key IN must contain an array of identifiers",
727 ),
728 );
729 }
730 }
731
732 QuerySyntaxSimpleValidationResult::default()
733 }
734}
735
736impl From<InternalClauses> for Vec<WhereClause> {
737 fn from(clauses: InternalClauses) -> Self {
738 let mut result: Self = clauses.equal_clauses.into_values().collect();
739
740 result.extend(clauses.in_clauses);
741 if let Some(clause) = clauses.primary_key_equal_clause {
742 result.push(clause);
743 };
744 if let Some(clause) = clauses.primary_key_in_clause {
745 result.push(clause);
746 };
747 if let Some(clause) = clauses.range_clause {
748 result.push(clause);
749 };
750
751 result
752 }
753}
754
755#[cfg(any(feature = "server", feature = "verify"))]
765#[derive(Copy, Clone, Debug, PartialEq, Eq)]
766#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
767#[cfg_attr(feature = "serde", serde(rename_all = "lowercase"))]
768pub enum TimeRangeSelector {
769 Newest,
772 Oldest,
775 ByStart {
783 start_ms: u64,
785 },
786}
787
788#[cfg(any(feature = "server", feature = "verify"))]
789impl TimeRangeSelector {
790 pub fn as_str(&self) -> &'static str {
800 match self {
801 TimeRangeSelector::Newest => "newest",
802 TimeRangeSelector::Oldest => "oldest",
803 TimeRangeSelector::ByStart { .. } => "byStart",
804 }
805 }
806
807 pub fn from_string(value: &str) -> Option<Self> {
811 match value {
812 "newest" => Some(TimeRangeSelector::Newest),
813 "oldest" => Some(TimeRangeSelector::Oldest),
814 _ => None,
815 }
816 }
817}
818
819#[cfg(any(feature = "server", feature = "verify"))]
828#[derive(Debug, Clone, Copy, PartialEq, Eq)]
829#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
830pub struct TimeRangeGridSpec {
831 pub range_seconds: u64,
833 pub step_seconds: u64,
835 pub phase_seconds: u64,
837}
838
839#[cfg(any(feature = "server", feature = "verify"))]
840impl TimeRangeGridSpec {
841 pub fn matches(&self, transform: &TimeRangeTransform) -> bool {
843 self.range_seconds == transform.range_seconds
844 && self.step_seconds == transform.step_seconds
845 && self.phase_seconds == transform.phase_seconds
846 }
847}
848
849#[cfg(any(feature = "server", feature = "verify"))]
862#[derive(Debug, Clone, PartialEq)]
863pub struct ResolvedTimeRange {
864 pub transform: IndexBucketing,
868}
869
870#[cfg(any(feature = "server", feature = "verify"))]
871impl ResolvedTimeRange {
872 pub fn field(&self) -> &str {
875 self.transform.source()
876 }
877
878 pub fn kind(&self) -> &'static str {
881 match self.transform {
882 IndexBucketing::Time(_) => "time-range",
883 IndexBucketing::Integer(_) => "integer-range",
884 }
885 }
886
887 pub fn operator(&self) -> &'static str {
889 match self.transform {
890 IndexBucketing::Time(_) => "IN_TIME_RANGE",
891 IndexBucketing::Integer(_) => "IN_INTEGER_RANGE",
892 }
893 }
894}
895
896#[cfg(any(feature = "server", feature = "verify"))]
898struct SelectionGridKind {
899 kind: &'static str,
901 operator: &'static str,
903 units: &'static str,
905}
906
907#[cfg(any(feature = "server", feature = "verify"))]
914fn select_selection_grid<'a, T>(
915 document_type: &'a DocumentTypeRef<'_>,
916 field: &str,
917 grid_of: impl Fn(&'a Index) -> Option<&'a T>,
918 spec: Option<(impl Fn(&T) -> bool, String)>,
919 names: SelectionGridKind,
920) -> Result<&'a T, Error>
921where
922 T: BucketSource + PartialEq,
923{
924 let mut grids: Vec<&'a T> = Vec::new();
925 for index in document_type.indexes().values() {
926 if let Some(transform) = grid_of(index).filter(|transform| transform.source() == field) {
927 if !grids.contains(&transform) {
928 grids.push(transform);
929 }
930 }
931 }
932 let SelectionGridKind {
933 kind,
934 operator,
935 units,
936 } = names;
937 let Some(first) = grids.first().copied() else {
938 return Err(Error::Query(
939 QuerySyntaxError::WhereClauseOnNonIndexedProperty(format!(
940 "no {kind} index is defined on field \"{field}\""
941 )),
942 ));
943 };
944 match spec {
945 Some((matches, described)) => grids
946 .into_iter()
947 .find(|transform| matches(transform))
948 .ok_or(Error::Query(QuerySyntaxError::Unsupported(format!(
949 "no {kind} index on \"{field}\" declares the grid {described}"
950 )))),
951 None if grids.len() > 1 => Err(Error::Query(QuerySyntaxError::Unsupported(format!(
952 "field \"{field}\" is bucketed by {} different {kind} grids; the {operator} \
953 selection must name one in its `grid` (range/step/phase{units} as the contract \
954 declares them)",
955 grids.len()
956 )))),
957 None => Ok(first),
958 }
959}
960
961#[cfg(any(feature = "server", feature = "verify"))]
963trait BucketSource {
964 fn source(&self) -> &str;
965}
966
967#[cfg(any(feature = "server", feature = "verify"))]
968impl BucketSource for TimeRangeTransform {
969 fn source(&self) -> &str {
970 &self.source
971 }
972}
973
974#[cfg(any(feature = "server", feature = "verify"))]
975impl BucketSource for IntegerRangeTransform {
976 fn source(&self) -> &str {
977 &self.source
978 }
979}
980
981#[cfg(any(feature = "server", feature = "verify"))]
1009pub fn resolve_time_range_bucket_clause(
1010 field: &str,
1011 selector: TimeRangeSelector,
1012 grid: Option<TimeRangeGridSpec>,
1013 document_type: DocumentTypeRef,
1014 block_time_ms: u64,
1015) -> Result<(WhereClause, ResolvedTimeRange), Error> {
1016 let transform = select_selection_grid(
1017 &document_type,
1018 field,
1019 |index| index.time_range.as_ref(),
1020 grid.map(|spec| {
1021 (
1022 move |transform: &TimeRangeTransform| spec.matches(transform),
1023 format!(
1024 "range={}s step={}s phase={}s",
1025 spec.range_seconds, spec.step_seconds, spec.phase_seconds
1026 ),
1027 )
1028 }),
1029 SelectionGridKind {
1030 kind: "time-range",
1031 operator: "IN_TIME_RANGE",
1032 units: ", in seconds,",
1033 },
1034 )?;
1035
1036 let bucket_start = match selector {
1037 TimeRangeSelector::Newest => transform.newest_active_start(block_time_ms),
1038 TimeRangeSelector::Oldest => transform.oldest_active_start(block_time_ms),
1039 TimeRangeSelector::ByStart { start_ms } => {
1045 if !transform.is_bucket_start(start_ms) {
1046 return Err(Error::Query(QuerySyntaxError::Unsupported(format!(
1047 "byStart {} on \"{}\" is not a window start of the grid range={}s \
1048 step={}s phase={}s: starts are phase + k*step on the millisecond \
1049 timeline, and an off-grid start is rejected rather than snapped",
1050 start_ms,
1051 field,
1052 transform.range_seconds,
1053 transform.step_seconds,
1054 transform.phase_seconds
1055 ))));
1056 }
1057 if transform.bucket_expired(start_ms, block_time_ms) {
1067 return Err(Error::Query(QuerySyntaxError::Unsupported(format!(
1068 "byStart {} on \"{}\" is past the ttl horizon ({}s): expired windows \
1069 drain lazily and may be mid-removal, so they are not queryable — \
1070 entries under this index live at most `ttl` past their window's start",
1071 start_ms,
1072 field,
1073 transform.ttl_seconds.unwrap_or_default()
1074 ))));
1075 }
1076 Some(start_ms)
1077 }
1078 }
1079 .ok_or(Error::Query(QuerySyntaxError::Unsupported(format!(
1080 "no time range on \"{}\" is active yet: the block time predates the grid's phase \
1081 anchor (only possible within the first step after the epoch)",
1082 field
1083 ))))?;
1084
1085 Ok((
1086 WhereClause {
1087 field: field.to_string(),
1088 operator: WhereOperator::Equal,
1089 value: Value::U64(bucket_start),
1090 },
1091 ResolvedTimeRange {
1092 transform: transform.clone().into(),
1093 },
1094 ))
1095}
1096
1097#[cfg(any(feature = "server", feature = "verify"))]
1103#[derive(Debug, Clone, Copy, PartialEq, Eq)]
1104#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
1105pub struct IntegerRangeGridSpec {
1106 pub range: u64,
1108 pub step: u64,
1110 pub phase: u64,
1112}
1113
1114#[cfg(any(feature = "server", feature = "verify"))]
1115impl IntegerRangeGridSpec {
1116 pub fn matches(&self, transform: &IntegerRangeTransform) -> bool {
1118 self.range == transform.range
1119 && self.step == transform.step
1120 && self.phase == transform.phase
1121 }
1122}
1123
1124#[cfg(any(feature = "server", feature = "verify"))]
1145pub fn resolve_integer_range_bucket_clause(
1146 field: &str,
1147 start: &Value,
1148 grid: Option<IntegerRangeGridSpec>,
1149 document_type: DocumentTypeRef,
1150) -> Result<(WhereClause, ResolvedTimeRange), Error> {
1151 let transform = select_selection_grid(
1152 &document_type,
1153 field,
1154 |index| index.integer_range.as_ref(),
1155 grid.map(|spec| {
1156 (
1157 move |transform: &IntegerRangeTransform| spec.matches(transform),
1158 format!(
1159 "range={} step={} phase={}",
1160 spec.range, spec.step, spec.phase
1161 ),
1162 )
1163 }),
1164 SelectionGridKind {
1165 kind: "integer-range",
1166 operator: "IN_INTEGER_RANGE",
1167 units: "",
1168 },
1169 )?;
1170
1171 let start = start.as_integer::<i128>().ok_or(Error::Query(
1172 QuerySyntaxError::InvalidWhereClauseComponents(
1173 "an IN_INTEGER_RANGE selection's start must be an integer",
1174 ),
1175 ))?;
1176 if !transform.is_window_start(start) {
1177 return Err(Error::Query(QuerySyntaxError::Unsupported(format!(
1178 "{} on \"{}\" is not a window start of the grid range={} step={} phase={}: starts \
1179 are phase + k*step within the property's integer type (or the type's minimum, for \
1180 the clamped bottom window), and an off-grid start is rejected rather than snapped",
1181 start, field, transform.range, transform.step, transform.phase
1182 ))));
1183 }
1184 let value = transform.key_type.value_of(start).ok_or(Error::Drive(
1187 DriveError::CorruptedCodeExecution("a window start is held by its key type"),
1188 ))?;
1189
1190 Ok((
1191 WhereClause {
1192 field: field.to_string(),
1193 operator: WhereOperator::Equal,
1194 value,
1195 },
1196 ResolvedTimeRange {
1197 transform: transform.clone().into(),
1198 },
1199 ))
1200}
1201
1202#[cfg(any(feature = "server", feature = "verify"))]
1231pub fn index_admissible_for_resolved_time_range(
1232 index: &Index,
1233 resolved_time_ranges: &[ResolvedTimeRange],
1234) -> bool {
1235 match resolved_time_ranges {
1236 [] => !index.is_bucketed(),
1237 [resolved] => index.is_bucketed_by(&resolved.transform),
1244 _ => false,
1245 }
1246}
1247
1248#[cfg(any(feature = "server", feature = "verify"))]
1251#[derive(Clone, Copy, Debug, PartialEq, Eq)]
1252pub struct SkipIfAbsentBinding<'a> {
1253 pub field: &'a str,
1255 pub excludes_missing: bool,
1260}
1261
1262#[cfg(any(feature = "server", feature = "verify"))]
1263impl<'a> SkipIfAbsentBinding<'a> {
1264 pub fn ordering(field: &'a str) -> Self {
1267 Self {
1268 field,
1269 excludes_missing: false,
1270 }
1271 }
1272
1273 pub fn for_where_clause(clause: &'a WhereClause) -> Self {
1279 let lower_bound_excludes_missing = |value: &Value| {
1280 value
1281 .as_array()
1282 .and_then(|bounds| bounds.first())
1283 .is_some_and(|lower| !may_encode_as_missing(lower))
1284 };
1285 let excludes_missing = match clause.operator {
1286 WhereOperator::Equal | WhereOperator::GreaterThanOrEquals => {
1287 !may_encode_as_missing(&clause.value)
1288 }
1289 WhereOperator::In => clause
1292 .in_values()
1293 .data
1294 .is_some_and(|values| values.iter().all(|value| !may_encode_as_missing(value))),
1295 WhereOperator::GreaterThan
1296 | WhereOperator::BetweenExcludeBounds
1297 | WhereOperator::BetweenExcludeLeft => true,
1298 WhereOperator::Between | WhereOperator::BetweenExcludeRight => {
1299 lower_bound_excludes_missing(&clause.value)
1300 }
1301 WhereOperator::StartsWith => clause
1302 .value
1303 .as_text()
1304 .is_some_and(|prefix| !prefix.is_empty()),
1305 WhereOperator::LessThan | WhereOperator::LessThanOrEquals => false,
1306 };
1307 Self {
1308 field: clause.field.as_str(),
1309 excludes_missing,
1310 }
1311 }
1312
1313 pub fn for_where_clauses(where_clauses: &'a [WhereClause]) -> Vec<Self> {
1315 where_clauses.iter().map(Self::for_where_clause).collect()
1316 }
1317}
1318
1319#[cfg(any(feature = "server", feature = "verify"))]
1325fn may_encode_as_missing(value: &Value) -> bool {
1326 match value {
1327 Value::Null => true,
1328 Value::Bytes(bytes) => bytes.is_empty(),
1329 Value::Array(values) => values.is_empty(),
1330 Value::Text(text) => text.is_empty(),
1331 _ => false,
1332 }
1333}
1334
1335#[cfg(any(feature = "server", feature = "verify"))]
1363pub fn index_admissible_for_skip_if_absent(
1364 index: &Index,
1365 bindings: &[SkipIfAbsentBinding<'_>],
1366) -> bool {
1367 if !index.skip_if_absent {
1368 return true;
1369 }
1370 let index_only = index.is_index_only();
1374 index.skip_if_absent_properties.iter().all(|skip_property| {
1375 bindings.iter().any(|binding| {
1376 binding.field == skip_property.as_str() && (index_only || binding.excludes_missing)
1377 })
1378 })
1379}
1380
1381#[cfg(any(feature = "server", feature = "verify"))]
1389pub fn index_admissible_for_query(
1390 index: &Index,
1391 resolved_time_ranges: &[ResolvedTimeRange],
1392 skip_bindings: &[SkipIfAbsentBinding<'_>],
1393) -> bool {
1394 index_admissible_for_resolved_time_range(index, resolved_time_ranges)
1395 && index_admissible_for_skip_if_absent(index, skip_bindings)
1396}
1397
1398#[cfg(any(feature = "server", feature = "verify"))]
1407pub fn document_index_admissible_for_query(
1408 index: &Index,
1409 resolved_time_ranges: &[ResolvedTimeRange],
1410 skip_bindings: &[SkipIfAbsentBinding<'_>],
1411) -> bool {
1412 !index.is_summable_off_count_index()
1413 && index_admissible_for_query(index, resolved_time_ranges, skip_bindings)
1414}
1415
1416#[cfg(any(feature = "server", feature = "verify"))]
1424pub fn pins_reach_chain(index: &Index, pin_depth: usize, chain_position: Option<usize>) -> bool {
1425 pin_depth >= 1
1426 && pin_depth < index.properties.len()
1427 && chain_position.is_some_and(|min_at| min_at < pin_depth)
1428}
1429
1430#[cfg(any(feature = "server", feature = "verify"))]
1454pub(crate) fn index_keeps_empty_groups(document_type: DocumentTypeRef, index: &Index) -> bool {
1455 document_type.indexes().values().any(|other| {
1456 (other.preallocated || other.outlives_delete)
1457 && other.shares_leading_levels(index, index.properties.len())
1458 })
1459}
1460
1461#[cfg(feature = "server")]
1465pub(crate) fn is_absent_path(error: &Error) -> bool {
1466 matches!(
1467 error,
1468 Error::GroveDB(e) if matches!(
1469 e.as_ref(),
1470 grovedb::Error::PathKeyNotFound(_)
1471 | grovedb::Error::PathNotFound(_)
1472 | grovedb::Error::PathParentLayerNotFound(_)
1473 )
1474 )
1475}
1476
1477#[cfg(feature = "server")]
1482pub fn uncovered_required_property_refusal() -> QuerySyntaxError {
1483 QuerySyntaxError::Unsupported(
1484 "this indexOnly query's index does not cover every required property, so the \
1485 documents it synthesizes cannot be serialized into a non-proof response; query \
1486 through an index covering all properties, or use a proved query"
1487 .to_string(),
1488 )
1489}
1490
1491#[cfg(feature = "server")]
1498pub fn index_only_serialization_refusal(error: &ProtocolError) -> Option<QuerySyntaxError> {
1499 matches!(
1500 error,
1501 ProtocolError::DataContractError(
1502 dpp::data_contract::errors::DataContractError::MissingRequiredKey(_)
1503 )
1504 )
1505 .then(uncovered_required_property_refusal)
1506}
1507
1508#[cfg(feature = "server")]
1530pub(crate) fn aggregate_or_zero_when_absent<T: Default>(
1531 drive: &Drive,
1532 path: &[Vec<u8>],
1533 value: Result<T, grovedb::Error>,
1534 range_total_verifier: FeatureVersion,
1535 transaction: TransactionArg,
1536 platform_version: &PlatformVersion,
1537) -> Result<T, Error> {
1538 match (value, range_total_verifier) {
1539 (value, 0) => value.map_err(|e| Error::GroveDB(Box::new(e))),
1540 (Err(error @ grovedb::Error::InvalidParentLayerPath(_)), 1) => {
1541 for depth in 0..path.len() {
1542 let found = drive
1543 .grove
1544 .get_raw_optional(
1545 path[..depth].into(),
1546 &path[depth],
1547 transaction,
1548 &platform_version.drive.grove_version,
1549 )
1550 .unwrap()
1551 .map_err(|e| Error::GroveDB(Box::new(e)))?;
1552 if found.is_none() {
1553 return Ok(T::default());
1554 }
1555 }
1556 Err(Error::GroveDB(Box::new(error)))
1557 }
1558 (value, 1) => value.map_err(|e| Error::GroveDB(Box::new(e))),
1559 (_, version) => Err(Error::Drive(DriveError::UnknownVersionMismatch {
1560 method: "aggregate_or_zero_when_absent".to_string(),
1561 known_versions: vec![0, 1],
1562 received: version,
1563 })),
1564 }
1565}
1566
1567#[cfg(any(feature = "server", feature = "verify"))]
1580pub(crate) fn prefix_to_last_path_query(
1581 base_path: Vec<Vec<u8>>,
1582 in_outer_keys: Option<Vec<Vec<u8>>>,
1583 subquery_path_extension: Vec<Vec<u8>>,
1584 terminal_level_key: Vec<u8>,
1585) -> PathQuery {
1586 match in_outer_keys {
1587 None => {
1588 let mut query = Query::new();
1589 query.insert_key(terminal_level_key);
1590 PathQuery::new(base_path, SizedQuery::new(query, None, None))
1591 }
1592 Some(keys) => {
1593 let mut outer_query = Query::new();
1594 for key in keys {
1595 outer_query.insert_key(key);
1596 }
1597 let mut subquery = Query::new();
1598 subquery.insert_key(terminal_level_key);
1599 if !subquery_path_extension.is_empty() {
1600 outer_query.set_subquery_path(subquery_path_extension);
1601 }
1602 outer_query.set_subquery(subquery);
1603 PathQuery::new(base_path, SizedQuery::new(outer_query, None, None))
1604 }
1605 }
1606}
1607
1608#[cfg(any(feature = "server", feature = "verify"))]
1629pub fn refuse_a_range_total_through_a_ranked_index(
1630 document_type: DocumentTypeRef,
1631 index: &Index,
1632) -> Result<(), Error> {
1633 let mut level = document_type.index_structure();
1634 for (position, property) in index.properties.iter().enumerate() {
1635 let Some(sub_level) = level
1636 .sub_levels()
1637 .get(&index.level_key(position, &property.name))
1638 else {
1639 return Ok(());
1640 };
1641 if !property_name_tree_type_and_ranked_axes_for_level(sub_level)?
1642 .1
1643 .is_empty()
1644 {
1645 let last = index
1646 .properties
1647 .last()
1648 .map(|property| property.name.as_str())
1649 .unwrap_or_default();
1650 return Err(Error::Query(QuerySyntaxError::Unsupported(format!(
1651 "a range total over the index `{}` is not available: its path passes through a \
1652 ranked level, and a range total is read only through unranked trees; group by \
1653 `{last}` to read each value in the range",
1654 index.name
1655 ))));
1656 }
1657 level = sub_level;
1658 }
1659 Ok(())
1660}
1661
1662#[cfg(any(feature = "server", feature = "verify"))]
1677pub fn validate_resolved_time_range_clause_shapes(
1678 where_clauses: &[WhereClause],
1679 resolved_time_ranges: &[ResolvedTimeRange],
1680) -> Result<(), Error> {
1681 for field in resolved_time_ranges.iter().map(|resolved| resolved.field()) {
1682 let mut equalities = 0usize;
1683 for clause in where_clauses.iter().filter(|c| c.field == field) {
1684 if clause.operator == WhereOperator::Equal {
1685 equalities += 1;
1686 } else {
1687 return Err(Error::Query(
1688 QuerySyntaxError::InvalidWhereClauseComponents(
1689 "a field resolved from a window selection (IN_TIME_RANGE or \
1690 IN_INTEGER_RANGE) may only carry the single equality its resolution \
1691 produced, not a range or In clause",
1692 ),
1693 ));
1694 }
1695 }
1696 if equalities != 1 {
1697 return Err(Error::Query(
1698 QuerySyntaxError::InvalidWhereClauseComponents(
1699 "a field resolved from a window selection (IN_TIME_RANGE or \
1700 IN_INTEGER_RANGE) must carry exactly one equality clause — the one its \
1701 resolution produced",
1702 ),
1703 ));
1704 }
1705 }
1706 Ok(())
1707}
1708
1709#[cfg(any(feature = "server", feature = "verify"))]
1710#[derive(Debug, PartialEq, Clone)]
1712pub struct DriveDocumentQuery<'a> {
1713 pub contract: &'a DataContract,
1715 pub document_type: DocumentTypeRef<'a>,
1717 pub internal_clauses: InternalClauses,
1719 pub offset: Option<u16>,
1721 pub limit: Option<u16>,
1723 pub order_by: IndexMap<String, OrderClause>,
1725 pub start_at: Option<[u8; 32]>,
1727 pub start_at_included: bool,
1729 pub block_time_ms: Option<u64>,
1731 pub resolved_time_ranges: Vec<ResolvedTimeRange>,
1747 pub sub_queries: Vec<DriveSubQuery<'a>>,
1766}
1767
1768impl<'a> DriveDocumentQuery<'a> {
1769 #[cfg(any(feature = "server", feature = "verify"))]
1771 pub fn new_primary_key_single_item_query(
1772 contract: &'a DataContract,
1773 document_type: DocumentTypeRef<'a>,
1774 id: Identifier,
1775 ) -> Self {
1776 DriveDocumentQuery {
1777 contract,
1778 document_type,
1779 internal_clauses: InternalClauses {
1780 primary_key_in_clause: None,
1781 primary_key_equal_clause: Some(WhereClause {
1782 field: document::property_names::ID.to_string(),
1783 operator: WhereOperator::Equal,
1784 value: Value::Identifier(id.to_buffer()),
1785 }),
1786 in_clauses: Vec::new(),
1787 range_clause: None,
1788 equal_clauses: Default::default(),
1789 },
1790 offset: None,
1791 limit: None,
1792 order_by: Default::default(),
1793 start_at: None,
1794 start_at_included: false,
1795 block_time_ms: None,
1796 resolved_time_ranges: vec![],
1797 sub_queries: vec![],
1798 }
1799 }
1800
1801 #[cfg(feature = "server")]
1802 pub fn any_item_query(contract: &'a DataContract, document_type: DocumentTypeRef<'a>) -> Self {
1804 DriveDocumentQuery {
1805 contract,
1806 document_type,
1807 internal_clauses: Default::default(),
1808 offset: None,
1809 limit: Some(1),
1810 order_by: Default::default(),
1811 start_at: None,
1812 start_at_included: true,
1813 block_time_ms: None,
1814 resolved_time_ranges: vec![],
1815 sub_queries: vec![],
1816 }
1817 }
1818
1819 #[cfg(feature = "server")]
1820 pub fn all_items_query(
1822 contract: &'a DataContract,
1823 document_type: DocumentTypeRef<'a>,
1824 limit: Option<u16>,
1825 ) -> Self {
1826 DriveDocumentQuery {
1827 contract,
1828 document_type,
1829 internal_clauses: Default::default(),
1830 offset: None,
1831 limit,
1832 order_by: Default::default(),
1833 start_at: None,
1834 start_at_included: true,
1835 block_time_ms: None,
1836 resolved_time_ranges: vec![],
1837 sub_queries: vec![],
1838 }
1839 }
1840
1841 #[cfg(any(feature = "server", feature = "verify"))]
1842 pub fn with_sub_queries(mut self, sub_queries: Vec<DriveSubQuery<'a>>) -> Self {
1847 self.sub_queries = sub_queries;
1848 self
1849 }
1850
1851 #[cfg(any(feature = "server", feature = "verify"))]
1852 pub fn with_by_id_join(
1865 mut self,
1866 source_property: impl Into<String>,
1867 document_type: DocumentTypeRef<'a>,
1868 ) -> Self {
1869 self.sub_queries.push(DriveSubQuery {
1870 contract: self.contract,
1871 document_type,
1872 kind: SubQueryKind::Documents,
1873 where_clauses: vec![],
1874 order_by: vec![],
1875 limit: None,
1876 binding: Some(SubQueryBinding {
1877 source: BindingSource::Page,
1878 source_property: source_property.into(),
1879 field: document::property_names::ID.to_string(),
1880 }),
1881 });
1882 self
1883 }
1884
1885 #[cfg(any(feature = "server", feature = "verify"))]
1886 pub(crate) fn ensure_no_sub_queries(&self, surface: &str) -> Result<(), Error> {
1891 if self.sub_queries.is_empty() {
1892 return Ok(());
1893 }
1894 Err(Error::Query(QuerySyntaxError::Unsupported(format!(
1895 "this query carries {} sub-queries, which {} would silently ignore; execute and \
1896 verify it on the composite surface (query_composite_documents / \
1897 verify_composite_documents_proof) or, for a single by-id join, the chained one \
1898 (query_chained_documents / verify_chained_documents_proof)",
1899 self.sub_queries.len(),
1900 surface,
1901 ))))
1902 }
1903
1904 #[cfg(any(feature = "server", feature = "verify"))]
1905 pub fn is_for_primary_key(&self) -> bool {
1907 self.internal_clauses.is_for_primary_key()
1908 || (self.internal_clauses.is_empty()
1909 && (self.order_by.is_empty()
1910 || (self.order_by.len() == 1
1911 && self
1912 .order_by
1913 .keys()
1914 .collect::<Vec<&String>>()
1915 .first()
1916 .unwrap()
1917 .as_str()
1918 == "$id")))
1919 }
1920
1921 #[cfg(feature = "cbor_query")]
1922 pub fn from_cbor(
1924 query_cbor: &[u8],
1925 contract: &'a DataContract,
1926 document_type: DocumentTypeRef<'a>,
1927 config: &DriveConfig,
1928 platform_version: &PlatformVersion,
1929 ) -> Result<Self, Error> {
1930 let query_document_value: Value = ciborium::de::from_reader(query_cbor).map_err(|_| {
1931 Error::Query(QuerySyntaxError::DeserializationError(
1932 "unable to decode query from cbor".to_string(),
1933 ))
1934 })?;
1935 Self::from_value(
1936 query_document_value,
1937 contract,
1938 document_type,
1939 config,
1940 platform_version,
1941 )
1942 }
1943
1944 #[cfg(any(feature = "server", feature = "verify"))]
1945 pub fn from_value(
1947 query_value: Value,
1948 contract: &'a DataContract,
1949 document_type: DocumentTypeRef<'a>,
1950 config: &DriveConfig,
1951 platform_version: &PlatformVersion,
1952 ) -> Result<Self, Error> {
1953 let query_document: BTreeMap<String, Value> = query_value.into_btree_string_map()?;
1954 Self::from_btree_map_value(
1955 query_document,
1956 contract,
1957 document_type,
1958 config,
1959 platform_version,
1960 )
1961 }
1962
1963 #[cfg(any(feature = "server", feature = "verify"))]
1964 pub fn from_btree_map_value(
1966 mut query_document: BTreeMap<String, Value>,
1967 contract: &'a DataContract,
1968 document_type: DocumentTypeRef<'a>,
1969 config: &DriveConfig,
1970 platform_version: &PlatformVersion,
1971 ) -> Result<Self, Error> {
1972 if let Some(contract_id) = query_document
1973 .remove_optional_identifier("contract_id")
1974 .map_err(|e| Error::Protocol(Box::new(ProtocolError::ValueError(e))))?
1975 {
1976 if contract.id() != contract_id {
1977 return Err(ProtocolError::IdentifierError(format!(
1978 "data contract id mismatch, expected: {}, got: {}",
1979 contract.id(),
1980 contract_id
1981 ))
1982 .into());
1983 };
1984 }
1985
1986 if let Some(document_type_name) = query_document
1987 .remove_optional_string("document_type_name")
1988 .map_err(|e| Error::Protocol(Box::new(ProtocolError::ValueError(e))))?
1989 {
1990 if document_type.name() != &document_type_name {
1991 return Err(ProtocolError::IdentifierError(format!(
1992 "document type name mismatch, expected: {}, got: {}",
1993 document_type.name(),
1994 document_type_name
1995 ))
1996 .into());
1997 }
1998 }
1999
2000 let maybe_limit: Option<u16> = query_document
2001 .remove_optional_integer("limit")
2002 .map_err(|e| Error::Protocol(Box::new(ProtocolError::ValueError(e))))?;
2003
2004 let limit = maybe_limit
2005 .map_or(Some(config.default_query_limit), |limit_value| {
2006 if limit_value == 0 || limit_value > config.default_query_limit {
2007 None
2008 } else {
2009 Some(limit_value)
2010 }
2011 })
2012 .ok_or(Error::Query(QuerySyntaxError::InvalidLimit(format!(
2013 "limit greater than max limit {}",
2014 config.max_query_limit
2015 ))))?;
2016
2017 let offset: Option<u16> = query_document
2018 .remove_optional_integer("offset")
2019 .map_err(|e| Error::Protocol(Box::new(ProtocolError::ValueError(e))))?;
2020
2021 let block_time_ms: Option<u64> = query_document
2022 .remove_optional_integer("blockTime")
2023 .map_err(|e| Error::Protocol(Box::new(ProtocolError::ValueError(e))))?;
2024
2025 let all_where_clauses: Vec<WhereClause> =
2026 query_document
2027 .remove("where")
2028 .map_or(Ok(vec![]), |id_cbor| {
2029 if let Value::Array(clauses) = id_cbor {
2030 clauses
2031 .iter()
2032 .map(|where_clause| {
2033 if let Value::Array(clauses_components) = where_clause {
2034 WhereClause::from_components(clauses_components)
2035 } else {
2036 Err(Error::Query(QuerySyntaxError::InvalidFormatWhereClause(
2037 "where clause must be an array".to_string(),
2038 )))
2039 }
2040 })
2041 .collect::<Result<Vec<WhereClause>, Error>>()
2042 } else {
2043 Err(Error::Query(QuerySyntaxError::InvalidFormatWhereClause(
2044 "where clause must be an array".to_string(),
2045 )))
2046 }
2047 })?;
2048
2049 let internal_clauses =
2050 InternalClauses::extract_from_clauses(all_where_clauses, platform_version)?;
2051
2052 let start_at_option = query_document.remove("startAt");
2053 let start_after_option = query_document.remove("startAfter");
2054 if start_after_option.is_some() && start_at_option.is_some() {
2055 return Err(Error::Query(QuerySyntaxError::DuplicateStartConditions(
2056 "only one of startAt or startAfter should be provided",
2057 )));
2058 }
2059
2060 let mut start_at_included = true;
2061
2062 let mut start_option: Option<Value> = None;
2063
2064 if start_after_option.is_some() {
2065 start_option = start_after_option;
2066 start_at_included = false;
2067 } else if start_at_option.is_some() {
2068 start_option = start_at_option;
2069 start_at_included = true;
2070 }
2071
2072 let start_at: Option<[u8; 32]> = start_option
2073 .map(|v| {
2074 v.into_identifier()
2075 .map_err(|e| Error::Protocol(Box::new(ProtocolError::ValueError(e))))
2076 .map(|identifier| identifier.into_buffer())
2077 })
2078 .transpose()?;
2079
2080 let order_by: IndexMap<String, OrderClause> =
2081 query_document
2082 .remove("orderBy")
2083 .map_or(Ok(IndexMap::new()), |id_cbor| {
2084 if let Value::Array(clauses) = id_cbor {
2085 clauses
2086 .into_iter()
2087 .filter_map(|order_clause| {
2088 if let Value::Array(clauses_components) = order_clause {
2089 let order_clause =
2090 OrderClause::from_components(&clauses_components)
2091 .map_err(Error::from);
2092 match order_clause {
2093 Ok(order_clause) => {
2094 Some(Ok((order_clause.field.clone(), order_clause)))
2095 }
2096 Err(err) => Some(Err(err)),
2097 }
2098 } else {
2099 None
2100 }
2101 })
2102 .collect::<Result<IndexMap<String, OrderClause>, Error>>()
2103 } else {
2104 Err(Error::Query(QuerySyntaxError::InvalidOrderByProperties(
2105 "order clauses must be an array",
2106 )))
2107 }
2108 })?;
2109
2110 if !query_document.is_empty() {
2111 return Err(Error::Query(QuerySyntaxError::Unsupported(format!(
2112 "unsupported syntax in where clause: {:?}",
2113 query_document
2114 ))));
2115 }
2116
2117 Ok(DriveDocumentQuery {
2118 contract,
2119 document_type,
2120 internal_clauses,
2121 limit: Some(limit),
2122 offset,
2123 order_by,
2124 start_at,
2125 start_at_included,
2126 block_time_ms,
2127 resolved_time_ranges: vec![],
2128 sub_queries: vec![],
2129 })
2130 }
2131
2132 #[cfg(any(feature = "server", feature = "verify"))]
2133 #[allow(clippy::too_many_arguments)]
2135 pub fn from_decomposed_values(
2136 where_clause: Value,
2137 order_by: Option<Value>,
2138 maybe_limit: Option<u16>,
2139 start_at: Option<[u8; 32]>,
2140 start_at_included: bool,
2141 block_time_ms: Option<u64>,
2142 contract: &'a DataContract,
2143 document_type: DocumentTypeRef<'a>,
2144 config: &DriveConfig,
2145 platform_version: &PlatformVersion,
2146 ) -> Result<Self, Error> {
2147 let all_where_clauses: Vec<WhereClause> = match where_clause {
2148 Value::Null => Ok(vec![]),
2149 Value::Array(clauses) => clauses
2150 .iter()
2151 .map(|where_clause| {
2152 if let Value::Array(clauses_components) = where_clause {
2153 WhereClause::from_components(clauses_components)
2154 } else {
2155 Err(Error::Query(QuerySyntaxError::InvalidFormatWhereClause(
2156 "where clause must be an array".to_string(),
2157 )))
2158 }
2159 })
2160 .collect::<Result<Vec<WhereClause>, Error>>(),
2161 _ => Err(Error::Query(QuerySyntaxError::InvalidFormatWhereClause(
2162 "where clause must be an array".to_string(),
2163 ))),
2164 }?;
2165
2166 let order_by_clauses: Vec<OrderClause> = match order_by {
2174 None | Some(Value::Null) => Vec::new(),
2175 Some(Value::Array(clauses)) => clauses
2176 .iter()
2177 .map(|order_clause| match order_clause {
2178 Value::Array(components) => {
2179 OrderClause::from_components(components).map_err(|_| {
2180 Error::Query(QuerySyntaxError::InvalidOrderByProperties(
2181 "invalid order_by clause components",
2182 ))
2183 })
2184 }
2185 _ => Err(Error::Query(QuerySyntaxError::InvalidOrderByProperties(
2186 "order_by clause must be an array",
2187 ))),
2188 })
2189 .collect::<Result<Vec<_>, _>>()?,
2190 Some(_) => {
2191 return Err(Error::Query(QuerySyntaxError::InvalidOrderByProperties(
2192 "order_by must be an array",
2193 )));
2194 }
2195 };
2196
2197 Self::from_typed_clauses(
2198 all_where_clauses,
2199 order_by_clauses,
2200 maybe_limit,
2201 start_at,
2202 start_at_included,
2203 block_time_ms,
2204 contract,
2205 document_type,
2206 config,
2207 platform_version,
2208 )
2209 }
2210
2211 #[cfg(any(feature = "server", feature = "verify"))]
2231 #[allow(clippy::too_many_arguments)]
2232 pub fn from_typed_clauses(
2233 where_clauses: Vec<WhereClause>,
2234 order_by_clauses: Vec<OrderClause>,
2235 maybe_limit: Option<u16>,
2236 start_at: Option<[u8; 32]>,
2237 start_at_included: bool,
2238 block_time_ms: Option<u64>,
2239 contract: &'a DataContract,
2240 document_type: DocumentTypeRef<'a>,
2241 config: &DriveConfig,
2242 platform_version: &PlatformVersion,
2243 ) -> Result<Self, Error> {
2244 let limit = maybe_limit
2245 .map_or(Some(config.default_query_limit), |limit_value| {
2246 if limit_value == 0 || limit_value > config.default_query_limit {
2247 None
2248 } else {
2249 Some(limit_value)
2250 }
2251 })
2252 .ok_or(Error::Query(QuerySyntaxError::InvalidLimit(format!(
2253 "limit greater than max limit {}",
2254 config.max_query_limit
2255 ))))?;
2256
2257 let internal_clauses =
2258 InternalClauses::extract_from_clauses(where_clauses, platform_version)?;
2259
2260 let order_by: IndexMap<String, OrderClause> = order_by_clauses
2261 .into_iter()
2262 .map(|c| (c.field.clone(), c))
2263 .collect();
2264
2265 Ok(DriveDocumentQuery {
2266 contract,
2267 document_type,
2268 internal_clauses,
2269 offset: None,
2270 limit: Some(limit),
2271 order_by,
2272 start_at,
2273 start_at_included,
2274 block_time_ms,
2275 resolved_time_ranges: vec![],
2276 sub_queries: vec![],
2277 })
2278 }
2279
2280 #[cfg(any(feature = "server", feature = "verify"))]
2281 pub fn from_sql_expr(
2283 sql_string: &str,
2284 contract: &'a DataContract,
2285 config: Option<&DriveConfig>,
2286 platform_version: &PlatformVersion,
2287 ) -> Result<Self, Error> {
2288 let dialect: MySqlDialect = MySqlDialect {};
2289 let statements: Vec<Statement> = Parser::parse_sql(&dialect, sql_string)
2290 .map_err(|e| Error::Query(QuerySyntaxError::SQLParsingError(e)))?;
2291
2292 let first_statement =
2294 statements
2295 .first()
2296 .ok_or(Error::Query(QuerySyntaxError::InvalidSQL(
2297 "Issue parsing sql getting first statement".to_string(),
2298 )))?;
2299
2300 let query: &ast::Query = match first_statement {
2301 ast::Statement::Query(query_struct) => Some(query_struct),
2302 _ => None,
2303 }
2304 .ok_or(Error::Query(QuerySyntaxError::InvalidSQL(
2305 "Issue parsing sql: not a query".to_string(),
2306 )))?;
2307
2308 let max_limit = config
2309 .map(|config| config.max_query_limit)
2310 .unwrap_or(DriveConfig::default().max_query_limit);
2311
2312 let limit: u16 = if let Some(limit_expr) = &query.limit {
2313 match limit_expr {
2314 ast::Expr::Value(Number(num_string, _)) => {
2315 let cast_num_string: &String = num_string;
2316 let user_limit = cast_num_string.parse::<u16>().map_err(|e| {
2317 Error::Query(QuerySyntaxError::InvalidLimit(format!(
2318 "limit could not be parsed {}",
2319 e
2320 )))
2321 })?;
2322 if user_limit > max_limit {
2323 return Err(Error::Query(QuerySyntaxError::InvalidLimit(format!(
2324 "limit {} greater than max limit {}",
2325 user_limit, max_limit
2326 ))));
2327 }
2328 user_limit
2329 }
2330 result => {
2331 return Err(Error::Query(QuerySyntaxError::InvalidLimit(format!(
2332 "expression not a limit {}",
2333 result
2334 ))));
2335 }
2336 }
2337 } else {
2338 config
2339 .map(|config| config.default_query_limit)
2340 .unwrap_or(DriveConfig::default().default_query_limit)
2341 };
2342
2343 let order_by: IndexMap<String, OrderClause> = query
2344 .order_by
2345 .iter()
2346 .map(|order_exp: &OrderByExpr| {
2347 let ascending = order_exp.asc.is_none() || order_exp.asc.unwrap();
2348 let field = order_exp.expr.to_string();
2349 (field.clone(), OrderClause { field, ascending })
2350 })
2351 .collect::<IndexMap<String, OrderClause>>();
2352
2353 let select: &Select = match &*query.body {
2355 ast::SetExpr::Select(select) => Some(select),
2356 _ => None,
2357 }
2358 .ok_or(Error::Query(QuerySyntaxError::InvalidSQL(
2359 "Issue parsing sql: Not a select".to_string(),
2360 )))?;
2361
2362 let document_type_name = match &select
2364 .from
2365 .first()
2366 .ok_or(Error::Query(QuerySyntaxError::InvalidSQL(
2367 "Invalid query: missing from section".to_string(),
2368 )))?
2369 .relation
2370 {
2371 Table { name, .. } => name.0.first().as_ref().map(|identifier| &identifier.value),
2372 _ => None,
2373 }
2374 .ok_or(Error::Query(QuerySyntaxError::InvalidSQL(
2375 "Issue parsing sql: invalid from value".to_string(),
2376 )))?;
2377
2378 let document_type =
2379 contract
2380 .document_types()
2381 .get(document_type_name)
2382 .ok_or(Error::Query(QuerySyntaxError::DocumentTypeNotFound(
2383 "document type not found in contract",
2384 )))?;
2385
2386 let mut all_where_clauses: Vec<WhereClause> = Vec::new();
2396 let selection_tree = select.selection.as_ref();
2397
2398 if let Some(selection_tree) = selection_tree {
2400 WhereClause::build_where_clauses_from_operations(
2401 selection_tree,
2402 document_type,
2403 &mut all_where_clauses,
2404 )?;
2405 }
2406
2407 let internal_clauses =
2408 InternalClauses::extract_from_clauses(all_where_clauses, platform_version)?;
2409
2410 let start_at_option = None; let start_after_option = None; let mut start_at_included = true;
2413 let mut start_option: Option<Value> = None;
2414
2415 if start_after_option.is_some() {
2416 start_option = start_after_option;
2417 start_at_included = false;
2418 } else if start_at_option.is_some() {
2419 start_option = start_at_option;
2420 start_at_included = true;
2421 }
2422
2423 let start_at: Option<[u8; 32]> = start_option
2424 .map(|v| {
2425 v.into_identifier()
2426 .map_err(|e| Error::Protocol(Box::new(ProtocolError::ValueError(e))))
2427 .map(|identifier| identifier.into_buffer())
2428 })
2429 .transpose()?;
2430
2431 Ok(DriveDocumentQuery {
2432 contract,
2433 document_type: document_type.as_ref(),
2434 internal_clauses,
2435 offset: None,
2436 limit: Some(limit),
2437 order_by,
2438 start_at,
2439 start_at_included,
2440 block_time_ms: None,
2441 resolved_time_ranges: vec![],
2442 sub_queries: vec![],
2443 })
2444 }
2445
2446 #[cfg(feature = "cbor_query")]
2451 pub fn to_cbor(&self) -> Result<Vec<u8>, Error> {
2452 let data: BTreeMap<String, Value> = self.into();
2453 let cbor: BTreeMap<String, ciborium::Value> = Value::convert_to_cbor_map(data)?;
2454 let mut output = Vec::new();
2455
2456 ciborium::ser::into_writer(&cbor, &mut output)
2457 .map_err(|e| ProtocolError::PlatformSerializationError(e.to_string()))?;
2458 Ok(output)
2459 }
2460
2461 #[cfg(any(feature = "server", feature = "verify"))]
2462 pub fn start_at_document_path_and_key(&self, starts_at: &[u8; 32]) -> (Vec<Vec<u8>>, Vec<u8>) {
2464 if self.document_type.documents_keep_history() {
2465 let document_holding_path = self.contract.documents_with_history_primary_key_path(
2466 self.document_type.name().as_str(),
2467 starts_at,
2468 );
2469 (
2470 document_holding_path
2471 .into_iter()
2472 .map(|key| key.to_vec())
2473 .collect::<Vec<_>>(),
2474 vec![0],
2475 )
2476 } else {
2477 let document_holding_path = self
2478 .contract
2479 .documents_primary_key_path(self.document_type.name().as_str());
2480 (
2481 document_holding_path
2482 .into_iter()
2483 .map(|key| key.to_vec())
2484 .collect::<Vec<_>>(),
2485 starts_at.to_vec(),
2486 )
2487 }
2488 }
2489
2490 #[cfg(any(feature = "server", feature = "verify"))]
2491 pub fn validate_in_clause_shape(
2502 &self,
2503 platform_version: &PlatformVersion,
2504 ) -> Result<(), Error> {
2505 match platform_version
2506 .drive
2507 .methods
2508 .document
2509 .query
2510 .non_primary_key_path_query
2511 {
2512 0 => {
2513 if self.internal_clauses.in_clauses.len() > 1 {
2514 return Err(Error::Query(QuerySyntaxError::MultipleInClauses(
2515 "There should only be one in clause",
2516 )));
2517 }
2518 Ok(())
2519 }
2520 1 => {
2521 if self.internal_clauses.in_clauses.len() > 1 && self.start_at.is_some() {
2522 return Err(Error::Query(QuerySyntaxError::Unsupported(
2523 "startAt/startAfter is not supported with multiple in clauses".to_string(),
2524 )));
2525 }
2526 Ok(())
2527 }
2528 version => Err(Error::Drive(DriveError::UnknownVersionMismatch {
2529 method: "DriveDocumentQuery::validate_in_clause_shape".to_string(),
2530 known_versions: vec![0, 1],
2531 received: version,
2532 })),
2533 }
2534 }
2535
2536 #[cfg(feature = "server")]
2537 pub fn construct_path_query_operations(
2539 &self,
2540 drive: &Drive,
2541 include_start_at_for_proof: bool,
2542 transaction: TransactionArg,
2543 drive_operations: &mut Vec<LowLevelDriveOperation>,
2544 platform_version: &PlatformVersion,
2545 ) -> Result<PathQuery, Error> {
2546 self.validate_in_clause_shape(platform_version)?;
2547 {
2550 use dpp::data_contract::document_type::accessors::DocumentTypeV2Getters;
2551 if self.document_type.index_only()
2552 && self.is_for_primary_key()
2553 && !self.index_only_flat_scan_applies()
2554 {
2555 return Err(Error::Query(QuerySyntaxError::Unsupported(
2556 "indexOnly documents cannot be fetched by id: there is no primary-key \
2557 tree; query through one of the type's indexes"
2558 .to_string(),
2559 )));
2560 }
2561 if self.document_type.index_only() && self.start_at.is_some() {
2562 return Err(Error::Query(QuerySyntaxError::Unsupported(
2563 "startAt/startAfter cursors cannot address an indexOnly position (the \
2564 synthesized document id is a one-way hash of it); paginate with a \
2565 range clause on the terminal property instead — equality clauses on \
2566 the index's properties, `terminal > <last seen value>` ordered by the \
2567 terminal, and a limit"
2568 .to_string(),
2569 )));
2570 }
2571 }
2572 let drive_version = &platform_version.drive;
2573 let document_type_path = self
2575 .contract
2576 .document_type_path(self.document_type.name().as_str())
2577 .into_iter()
2578 .map(|a| a.to_vec())
2579 .collect::<Vec<Vec<u8>>>();
2580
2581 {
2586 use dpp::data_contract::document_type::accessors::DocumentTypeV2Getters;
2587 if self.document_type.index_only() {
2588 if let Some(path_query) =
2589 self.index_only_route(&document_type_path, platform_version)?
2590 {
2591 return Ok(path_query);
2592 }
2593 }
2594 }
2595
2596 let cursor_included = self.start_at_included || self.pads_cursor_page(platform_version);
2597 let (starts_at_document, start_at_path_query) = match &self.start_at {
2598 None => Ok((None, None)),
2599 Some(starts_at) => {
2600 let (start_at_document_path, start_at_document_key) =
2604 self.start_at_document_path_and_key(starts_at);
2605 let start_at_document = drive
2606 .grove_get(
2607 start_at_document_path.as_slice().into(),
2608 &start_at_document_key,
2609 StatefulQuery,
2610 transaction,
2611 drive_operations,
2612 drive_version,
2613 )
2614 .map_err(|e| match e {
2615 Error::GroveDB(e)
2616 if matches!(
2617 e.as_ref(),
2618 GroveError::PathKeyNotFound(_)
2619 | GroveError::PathNotFound(_)
2620 | GroveError::PathParentLayerNotFound(_)
2621 ) =>
2622 {
2623 let error_message = if self.start_at_included {
2624 "startAt document not found"
2625 } else {
2626 "startAfter document not found"
2627 };
2628
2629 Error::Query(QuerySyntaxError::StartDocumentNotFound(error_message))
2630 }
2631 _ => e,
2632 })?
2633 .ok_or(Error::Drive(DriveError::CorruptedCodeExecution(
2634 "expected a value",
2635 )))?;
2636
2637 let path_query =
2638 PathQuery::new_single_key(start_at_document_path, start_at_document_key);
2639
2640 if let Element::Item(item, _) = start_at_document {
2641 let document = Document::from_bytes(
2642 item.as_slice(),
2643 self.document_type,
2644 platform_version,
2645 )?;
2646 Ok((Some((document, cursor_included)), Some(path_query)))
2647 } else {
2648 Err(Error::Drive(DriveError::CorruptedDocumentPath(
2649 "Holding paths should only have items",
2650 )))
2651 }
2652 }
2653 }?;
2654 let mut main_path_query = if self.is_for_primary_key() {
2655 self.get_primary_key_path_query(
2656 document_type_path,
2657 starts_at_document,
2658 platform_version,
2659 )
2660 } else {
2661 self.get_non_primary_key_path_query(
2662 document_type_path,
2663 starts_at_document,
2664 platform_version,
2665 )
2666 }?;
2667 self.pad_cursor_page_limit(&mut main_path_query, platform_version)?;
2668 if !include_start_at_for_proof {
2669 return Ok(main_path_query);
2670 }
2671
2672 if let Some(mut start_at_path_query) = start_at_path_query {
2673 start_at_path_query.query.query.left_to_right =
2679 main_path_query.query.query.left_to_right;
2680 let limit = main_path_query.query.limit.take();
2681 let mut merged = PathQuery::merge(
2682 vec![&start_at_path_query, &main_path_query],
2683 &platform_version.drive.grove_version,
2684 )
2685 .map_err(Error::from)?;
2686 let cursor_on_page_layer = merged.path == main_path_query.path;
2695 let cursor_key_on_page_layer: Option<&[u8]> = if !cursor_on_page_layer {
2699 None
2700 } else if let Some(component) = start_at_path_query.path.get(merged.path.len()) {
2701 Some(component.as_slice())
2702 } else {
2703 match start_at_path_query.query.query.items.as_slice() {
2704 [QueryItem::Key(cursor_key)] => Some(cursor_key.as_slice()),
2705 _ => None,
2706 }
2707 };
2708 let cursor_row_in_page = cursor_key_on_page_layer.is_some_and(|cursor_key| {
2715 main_path_query
2716 .query
2717 .query
2718 .items
2719 .iter()
2720 .any(|item| item.contains(cursor_key))
2721 });
2722 merged.query.limit = limit.map(|a| {
2723 if cursor_row_in_page {
2724 a
2725 } else {
2726 a.saturating_add(1)
2727 }
2728 });
2729 if !cursor_on_page_layer {
2730 merged.query.query.left_to_right = true;
2752 }
2753 Ok(merged)
2754 } else {
2755 Ok(main_path_query)
2756 }
2757 }
2758
2759 #[cfg(any(feature = "server", feature = "verify"))]
2760 pub fn construct_path_query(
2762 &self,
2763 starts_at_document: Option<Document>,
2764 platform_version: &PlatformVersion,
2765 ) -> Result<PathQuery, Error> {
2766 self.validate_in_clause_shape(platform_version)?;
2767 {
2770 use dpp::data_contract::document_type::accessors::DocumentTypeV2Getters;
2771 if self.document_type.index_only()
2772 && self.is_for_primary_key()
2773 && !self.index_only_flat_scan_applies()
2774 {
2775 return Err(Error::Query(QuerySyntaxError::Unsupported(
2776 "indexOnly documents cannot be fetched by id: there is no primary-key \
2777 tree; query through one of the type's indexes"
2778 .to_string(),
2779 )));
2780 }
2781 if self.document_type.index_only() && self.start_at.is_some() {
2782 return Err(Error::Query(QuerySyntaxError::Unsupported(
2783 "startAt/startAfter cursors cannot address an indexOnly position (the \
2784 synthesized document id is a one-way hash of it); paginate with a \
2785 range clause on the terminal property instead — equality clauses on \
2786 the index's properties, `terminal > <last seen value>` ordered by the \
2787 terminal, and a limit"
2788 .to_string(),
2789 )));
2790 }
2791 }
2792 let document_type_path = self
2794 .contract
2795 .document_type_path(self.document_type.name().as_str())
2796 .into_iter()
2797 .map(|a| a.to_vec())
2798 .collect::<Vec<Vec<u8>>>();
2799
2800 {
2804 use dpp::data_contract::document_type::accessors::DocumentTypeV2Getters;
2805 if self.document_type.index_only() {
2806 if let Some(path_query) =
2807 self.index_only_route(&document_type_path, platform_version)?
2808 {
2809 return Ok(path_query);
2810 }
2811 }
2812 }
2813
2814 let cursor_included = self.start_at_included || self.pads_cursor_page(platform_version);
2815 let starts_at_document =
2816 starts_at_document.map(|starts_at_document| (starts_at_document, cursor_included));
2817 let mut path_query = if self.is_for_primary_key() {
2818 self.get_primary_key_path_query(
2819 document_type_path,
2820 starts_at_document,
2821 platform_version,
2822 )
2823 } else {
2824 self.get_non_primary_key_path_query(
2825 document_type_path,
2826 starts_at_document,
2827 platform_version,
2828 )
2829 }?;
2830 self.pad_cursor_page_limit(&mut path_query, platform_version)?;
2831 Ok(path_query)
2832 }
2833
2834 #[cfg(any(feature = "server", feature = "verify"))]
2835 pub fn pads_cursor_page(&self, platform_version: &PlatformVersion) -> bool {
2851 use dpp::data_contract::document_type::accessors::DocumentTypeV2Getters;
2852 self.start_at.is_some()
2853 && !self.start_at_included
2854 && !self.is_for_primary_key()
2855 && !self.document_type.index_only()
2856 && platform_version
2857 .drive
2858 .methods
2859 .document
2860 .query
2861 .non_primary_key_path_query
2862 >= 1
2863 }
2864
2865 #[cfg(any(feature = "server", feature = "verify"))]
2866 fn pad_cursor_page_limit(
2872 &self,
2873 path_query: &mut PathQuery,
2874 platform_version: &PlatformVersion,
2875 ) -> Result<(), Error> {
2876 if !self.pads_cursor_page(platform_version) {
2877 return Ok(());
2878 }
2879 let offset = path_query.query.offset.take().unwrap_or(0);
2880 if let Some(limit) = path_query.query.limit {
2881 let padded = limit
2882 .checked_add(1)
2883 .and_then(|limit| limit.checked_add(offset))
2884 .ok_or_else(|| {
2885 Error::Query(QuerySyntaxError::InvalidLimit(format!(
2886 "limit {limit} and offset {offset} are too large together with a \
2887 startAfter cursor"
2888 )))
2889 })?;
2890 path_query.query.limit = Some(padded);
2891 }
2892 Ok(())
2893 }
2894
2895 #[cfg(any(feature = "server", feature = "verify"))]
2896 pub(crate) fn strip_cursor_from_page(
2905 &self,
2906 mut serialized_documents: Vec<Vec<u8>>,
2907 platform_version: &PlatformVersion,
2908 ) -> Result<(Vec<Vec<u8>>, u16), Error> {
2909 use dpp::document::serialization_traits::DocumentPlatformConversionMethodsV0;
2910 use dpp::document::DocumentV0Getters;
2911 if !self.pads_cursor_page(platform_version) {
2912 return Ok((serialized_documents, 0));
2913 }
2914 let Some(start_at) = self.start_at else {
2915 return Ok((serialized_documents, 0));
2916 };
2917 if let Some(first) = serialized_documents.first() {
2918 let document = Document::from_bytes(first, self.document_type, platform_version)?;
2919 if document.id().to_buffer() == start_at {
2920 serialized_documents.remove(0);
2921 }
2922 }
2923 let skipped = (self.offset.unwrap_or(0) as usize).min(serialized_documents.len());
2924 serialized_documents.drain(..skipped);
2925 if let Some(limit) = self.limit {
2926 serialized_documents.truncate(limit as usize);
2927 }
2928 Ok((serialized_documents, skipped as u16))
2929 }
2930
2931 #[cfg(feature = "server")]
2932 fn strip_cursor_from_elements(
2934 &self,
2935 mut elements: QueryResultElements,
2936 platform_version: &PlatformVersion,
2937 ) -> Result<(QueryResultElements, u16), Error> {
2938 use dpp::document::DocumentV0Getters;
2939 use grovedb::query_result_type::QueryResultElement;
2940 if !self.pads_cursor_page(platform_version) {
2941 return Ok((elements, 0));
2942 }
2943 let Some(start_at) = self.start_at else {
2944 return Ok((elements, 0));
2945 };
2946 let first_element = match elements.elements.first() {
2947 Some(QueryResultElement::ElementResultItem(element))
2948 | Some(QueryResultElement::KeyElementPairResultItem((_, element)))
2949 | Some(QueryResultElement::PathKeyElementTrioResultItem((_, _, element))) => {
2950 Some(element)
2951 }
2952 None => None,
2953 };
2954 let first_is_cursor = match first_element {
2955 Some(Element::Item(bytes, _)) => {
2956 Document::from_bytes(bytes, self.document_type, platform_version)?
2957 .id()
2958 .to_buffer()
2959 == start_at
2960 }
2961 _ => false,
2962 };
2963 if first_is_cursor {
2964 elements.elements.remove(0);
2965 }
2966 let skipped = (self.offset.unwrap_or(0) as usize).min(elements.elements.len());
2967 elements.elements.drain(..skipped);
2968 if let Some(limit) = self.limit {
2969 elements.elements.truncate(limit as usize);
2970 }
2971 Ok((elements, skipped as u16))
2972 }
2973
2974 #[cfg(any(feature = "server", feature = "verify"))]
2975 pub fn get_primary_key_path_query(
2977 &self,
2978 document_type_path: Vec<Vec<u8>>,
2979 starts_at_document: Option<(Document, bool)>,
2980 platform_version: &PlatformVersion,
2981 ) -> Result<PathQuery, Error> {
2982 let mut path = document_type_path;
2983
2984 path.push(vec![0]);
2986
2987 if let Some(primary_key_equal_clause) = &self.internal_clauses.primary_key_equal_clause {
2988 let mut query = Query::new();
2989 let key = self.document_type.serialize_value_for_key(
2990 "$id",
2991 &primary_key_equal_clause.value,
2992 platform_version,
2993 )?;
2994 query.insert_key(key);
2995
2996 if self.document_type.documents_keep_history() {
2997 if let Some(block_time) = self.block_time_ms {
2999 let encoded_block_time = encode_u64(block_time);
3000 let mut sub_query = Query::new_with_direction(false);
3001 sub_query.insert_range_to_inclusive(..=encoded_block_time);
3002 query.set_subquery(sub_query);
3003 } else {
3004 query.set_subquery_key(vec![0]);
3005 }
3006 }
3007
3008 Ok(PathQuery::new(path, SizedQuery::new(query, Some(1), None)))
3009 } else {
3010 let left_to_right = if self.order_by.keys().len() == 1 {
3012 if self.order_by.keys().next().unwrap() != "$id" {
3013 return Err(Error::Query(QuerySyntaxError::InvalidOrderByProperties(
3014 "order by should include $id only",
3015 )));
3016 }
3017
3018 let order_clause = self.order_by.get("$id").unwrap();
3019
3020 order_clause.ascending
3021 } else {
3022 true
3023 };
3024
3025 let mut query = Query::new_with_direction(left_to_right);
3026 let starts_at_key_option = match starts_at_document {
3029 None => None,
3030 Some((document, included)) => {
3031 document
3033 .get_raw_for_document_type(
3034 "$id",
3035 self.document_type,
3036 None,
3037 platform_version,
3038 )?
3039 .map(|raw_value_option| (raw_value_option, included))
3040 }
3041 };
3042
3043 if let Some(primary_key_in_clause) = &self.internal_clauses.primary_key_in_clause {
3044 let in_values = primary_key_in_clause.in_values().into_data_with_error()??;
3045
3046 match starts_at_key_option {
3047 None => {
3048 for value in in_values.iter() {
3049 let key = self.document_type.serialize_value_for_key(
3050 "$id",
3051 value,
3052 platform_version,
3053 )?;
3054 query.insert_key(key)
3055 }
3056 }
3057 Some((starts_at_key, included)) => {
3058 for value in in_values.iter() {
3059 let key = self.document_type.serialize_value_for_key(
3060 "$id",
3061 value,
3062 platform_version,
3063 )?;
3064
3065 if (left_to_right && starts_at_key < key)
3066 || (!left_to_right && starts_at_key > key)
3067 || (included && starts_at_key == key)
3068 {
3069 query.insert_key(key);
3070 }
3071 }
3072 }
3073 }
3074
3075 if self.document_type.documents_keep_history() {
3076 if let Some(_block_time) = self.block_time_ms {
3078 return Err(Error::Query(QuerySyntaxError::Unsupported(
3080 "Not yet implemented".to_string(),
3081 )));
3082 } else {
3090 query.set_subquery_key(vec![0]);
3091 }
3092 }
3093
3094 Ok(PathQuery::new(
3095 path,
3096 SizedQuery::new(query, self.limit, self.offset),
3097 ))
3098 } else {
3099 match starts_at_key_option {
3101 None => {
3102 query.insert_all();
3103 }
3104 Some((starts_at_key, included)) => match left_to_right {
3105 true => match included {
3106 true => query.insert_range_from(starts_at_key..),
3107 false => query.insert_range_after(starts_at_key..),
3108 },
3109 false => match included {
3110 true => query.insert_range_to_inclusive(..=starts_at_key),
3111 false => query.insert_range_to(..starts_at_key),
3112 },
3113 },
3114 }
3115
3116 if self.document_type.documents_keep_history() {
3117 if let Some(_block_time) = self.block_time_ms {
3119 return Err(Error::Query(QuerySyntaxError::Unsupported(
3120 "this query is not supported".to_string(),
3121 )));
3122 } else {
3130 query.set_subquery_key(vec![0]);
3131 }
3132 }
3133
3134 Ok(PathQuery::new(
3135 path,
3136 SizedQuery::new(query, self.limit, self.offset),
3137 ))
3138 }
3139 }
3140 }
3141
3142 #[cfg(any(feature = "server", feature = "verify"))]
3143 pub fn find_best_index(&self, platform_version: &PlatformVersion) -> Result<&Index, Error> {
3158 match self.select_best_index(platform_version)? {
3159 BestIndexOutcome::Matched(index) => Ok(index),
3160 BestIndexOutcome::NoIndexMatches(no_index_error) => Err(no_index_error),
3161 }
3162 }
3163
3164 pub(crate) fn select_best_index(
3173 &self,
3174 platform_version: &PlatformVersion,
3175 ) -> Result<BestIndexOutcome<'_>, Error> {
3176 if self.resolved_time_ranges.len() > 1 {
3182 return Err(Error::Query(QuerySyntaxError::Unsupported(format!(
3183 "at most one window selection (IN_TIME_RANGE or IN_INTEGER_RANGE) is \
3184 supported per query; this one resolves {:?}, and no single index can bucket \
3185 more than one field",
3186 self.resolved_time_ranges
3187 ))));
3188 }
3189
3190 self.validate_resolved_source_shape()?;
3194
3195 if self.internal_clauses.in_clauses.len() > 1 {
3196 return Ok(BestIndexOutcome::Matched(
3200 self.find_best_index_for_multiple_in_clauses()?.0,
3201 ));
3202 }
3203
3204 let equal_fields = self
3205 .internal_clauses
3206 .equal_clauses
3207 .keys()
3208 .map(|s| s.as_str())
3209 .collect::<Vec<&str>>();
3210 let in_field = self
3211 .internal_clauses
3212 .in_clauses
3213 .first()
3214 .map(|in_clause| in_clause.field.as_str());
3215 let range_field = self
3216 .internal_clauses
3217 .range_clause
3218 .as_ref()
3219 .map(|range_clause| range_clause.field.as_str());
3220 let order_by_keys: Vec<&str> = self.order_by.keys().map(String::as_str).collect();
3221
3222 let skip_bindings = self
3228 .internal_clauses
3229 .skip_if_absent_bindings(&order_by_keys);
3230
3231 let Some((index, difference)) = self.document_type.index_for_types_matching(
3232 equal_fields.as_slice(),
3233 range_field,
3234 in_field,
3235 order_by_keys.as_slice(),
3236 |index| {
3240 document_index_admissible_for_query(
3241 index,
3242 &self.resolved_time_ranges,
3243 &skip_bindings,
3244 )
3245 },
3246 platform_version,
3247 )?
3248 else {
3249 return Ok(BestIndexOutcome::NoIndexMatches(
3250 match self.resolved_time_ranges.first() {
3251 Some(resolved) => {
3258 Error::Query(QuerySyntaxError::WhereClauseOnNonIndexedProperty(format!(
3259 "a {} query on \"{}\" requires an index that buckets it with \
3260 the resolved grid AND covers the query's other where and order-by \
3261 fields; valid indexes are: {:?}",
3262 resolved.kind(),
3263 resolved.field(),
3264 self.document_type.indexes()
3265 )))
3266 }
3267 None => {
3268 let has_bucketed_index = self
3274 .document_type
3275 .indexes()
3276 .values()
3277 .any(|index| index.is_bucketed());
3278 if has_bucketed_index {
3279 Error::Query(QuerySyntaxError::WhereClauseOnNonIndexedProperty(
3280 format!(
3281 "query must be for valid indexes, valid indexes are: {:?}; note: \
3282 this document type's bucketed (timeRange / integerRange) indexes \
3283 only serve IN_TIME_RANGE / IN_INTEGER_RANGE selections carrying \
3284 their resolution — a raw clause on the bucketed field never binds \
3285 to them",
3286 self.document_type.indexes()
3287 ),
3288 ))
3289 } else {
3290 Error::Query(QuerySyntaxError::WhereClauseOnNonIndexedProperty(
3291 format!(
3292 "query must be for valid indexes, valid indexes are: {:?}",
3293 self.document_type.indexes()
3294 ),
3295 ))
3296 }
3297 }
3298 },
3299 ));
3300 };
3301 if difference > defaults::MAX_INDEX_DIFFERENCE {
3302 return Ok(BestIndexOutcome::NoIndexMatches(Error::Query(
3303 QuerySyntaxError::QueryTooFarFromIndex("query must better match an existing index"),
3304 )));
3305 }
3306
3307 Ok(BestIndexOutcome::Matched(index))
3314 }
3315
3316 #[cfg(any(feature = "server", feature = "verify"))]
3331 pub(crate) fn validate_resolved_source_shape(&self) -> Result<(), Error> {
3332 let Some(resolved) = self.resolved_time_ranges.first() else {
3333 return Ok(());
3334 };
3335 let source = resolved.field();
3336 let has_equality_on_source = self.internal_clauses.equal_clauses.contains_key(source);
3337 let range_or_in_on_source = self
3338 .internal_clauses
3339 .range_clause
3340 .as_ref()
3341 .is_some_and(|clause| clause.field == source)
3342 || self
3343 .internal_clauses
3344 .in_clauses
3345 .iter()
3346 .any(|clause| clause.field == source);
3347 if !has_equality_on_source || range_or_in_on_source || self.order_by.contains_key(source) {
3348 return Err(Error::Query(QuerySyntaxError::Unsupported(format!(
3349 "the index on \"{source}\" buckets it into {kind} windows: it can only be \
3350 queried through a {kind} selection ({operator}, which resolves to an exact \
3351 window equality), not with ranges, IN, or ordering on that property",
3352 kind = resolved.kind(),
3353 operator = resolved.operator(),
3354 ))));
3355 }
3356 Ok(())
3357 }
3358
3359 #[cfg(any(feature = "server", feature = "verify"))]
3360 pub fn query_item_for_starts_at_key(starts_at_key: Vec<u8>, left_to_right: bool) -> QueryItem {
3362 if left_to_right {
3363 QueryItem::RangeAfter(starts_at_key..)
3364 } else {
3365 QueryItem::RangeTo(..starts_at_key)
3366 }
3367 }
3368
3369 #[cfg(any(feature = "server", feature = "verify"))]
3370 pub fn get_non_primary_key_path_query(
3377 &self,
3378 document_type_path: Vec<Vec<u8>>,
3379 starts_at_document: Option<(Document, bool)>,
3380 platform_version: &PlatformVersion,
3381 ) -> Result<PathQuery, Error> {
3382 let starts_at_document = match starts_at_document {
3383 Some((document, included)) => Some((
3384 self.cursor_with_derived_values(document, platform_version)?,
3385 included,
3386 )),
3387 None => None,
3388 };
3389 match platform_version
3390 .drive
3391 .methods
3392 .document
3393 .query
3394 .non_primary_key_path_query
3395 {
3396 0 => self.get_non_primary_key_path_query_v0(
3397 document_type_path,
3398 starts_at_document,
3399 platform_version,
3400 ),
3401 1 => self.get_non_primary_key_path_query_v1(
3402 document_type_path,
3403 starts_at_document,
3404 platform_version,
3405 ),
3406 version => Err(Error::Drive(DriveError::UnknownVersionMismatch {
3407 method: "DriveDocumentQuery::get_non_primary_key_path_query".to_string(),
3408 known_versions: vec![0, 1],
3409 received: version,
3410 })),
3411 }
3412 }
3413
3414 #[cfg(any(feature = "server", feature = "verify"))]
3423 fn cursor_with_derived_values(
3424 &self,
3425 mut document: Document,
3426 platform_version: &PlatformVersion,
3427 ) -> Result<Document, Error> {
3428 let derived_index_properties = self.document_type.derived_index_properties();
3429 if derived_index_properties.is_empty() {
3430 return Ok(document);
3431 }
3432 let index = self.find_best_index(platform_version)?;
3433 for property in &index.properties {
3434 if !derived_index_properties.contains_key(&property.name) {
3435 continue;
3436 }
3437 let Some(clause) = self.internal_clauses.equal_clauses.get(&property.name) else {
3438 return Err(Error::Query(QuerySyntaxError::Unsupported(format!(
3439 "a startAt or startAfter cursor is placed by the values the document it \
3440 names stores, and the index {} reads \"{}\" from the document a reference \
3441 points at instead: fix \"{}\" with == to page with a cursor, or page by a \
3442 range on another property of the index",
3443 index.name, property.name, property.name
3444 ))));
3445 };
3446 document
3447 .properties_mut()
3448 .insert(property.name.clone(), clause.value.clone());
3449 }
3450 Ok(document)
3451 }
3452
3453 #[cfg(feature = "server")]
3454 pub fn execute_with_proof(
3456 self,
3457 drive: &Drive,
3458 block_info: Option<BlockInfo>,
3459 transaction: TransactionArg,
3460 platform_version: &PlatformVersion,
3461 ) -> Result<(Vec<u8>, u64), Error> {
3462 self.ensure_no_sub_queries("execute_with_proof")?;
3463 let mut drive_operations = vec![];
3464 let items = self.execute_with_proof_internal(
3465 drive,
3466 transaction,
3467 &mut drive_operations,
3468 platform_version,
3469 )?;
3470 let cost = if let Some(block_info) = block_info {
3471 let fee_result = Drive::calculate_fee(
3472 None,
3473 Some(drive_operations),
3474 &block_info.epoch,
3475 drive.config.epochs_per_era,
3476 platform_version,
3477 None,
3478 )?;
3479 fee_result.processing_fee
3480 } else {
3481 0
3482 };
3483 Ok((items, cost))
3484 }
3485
3486 #[cfg(feature = "server")]
3487 pub(crate) fn execute_with_proof_internal(
3489 self,
3490 drive: &Drive,
3491 transaction: TransactionArg,
3492 drive_operations: &mut Vec<LowLevelDriveOperation>,
3493 platform_version: &PlatformVersion,
3494 ) -> Result<Vec<u8>, Error> {
3495 let path_query = self.construct_path_query_operations(
3496 drive,
3497 true,
3498 transaction,
3499 drive_operations,
3500 platform_version,
3501 )?;
3502 drive.grove_get_proved_path_query(
3503 &path_query,
3504 transaction,
3505 drive_operations,
3506 &platform_version.drive,
3507 )
3508 }
3509
3510 #[cfg(all(feature = "server", feature = "verify"))]
3511 pub fn execute_with_proof_only_get_elements(
3513 self,
3514 drive: &Drive,
3515 block_info: Option<BlockInfo>,
3516 transaction: TransactionArg,
3517 platform_version: &PlatformVersion,
3518 ) -> Result<(RootHash, Vec<Vec<u8>>, u64), Error> {
3519 self.ensure_no_sub_queries("execute_with_proof_only_get_elements")?;
3520 let mut drive_operations = vec![];
3521 let (root_hash, items) = self.execute_with_proof_only_get_elements_internal(
3522 drive,
3523 transaction,
3524 &mut drive_operations,
3525 platform_version,
3526 )?;
3527 let cost = if let Some(block_info) = block_info {
3528 let fee_result = Drive::calculate_fee(
3529 None,
3530 Some(drive_operations),
3531 &block_info.epoch,
3532 drive.config.epochs_per_era,
3533 platform_version,
3534 None,
3535 )?;
3536 fee_result.processing_fee
3537 } else {
3538 0
3539 };
3540 Ok((root_hash, items, cost))
3541 }
3542
3543 #[cfg(all(feature = "server", feature = "verify"))]
3544 pub(crate) fn execute_with_proof_only_get_elements_internal(
3546 self,
3547 drive: &Drive,
3548 transaction: TransactionArg,
3549 drive_operations: &mut Vec<LowLevelDriveOperation>,
3550 platform_version: &PlatformVersion,
3551 ) -> Result<(RootHash, Vec<Vec<u8>>), Error> {
3552 let path_query = self.construct_path_query_operations(
3553 drive,
3554 true,
3555 transaction,
3556 drive_operations,
3557 platform_version,
3558 )?;
3559
3560 let proof = drive.grove_get_proved_path_query(
3561 &path_query,
3562 transaction,
3563 drive_operations,
3564 &platform_version.drive,
3565 )?;
3566 self.verify_proof_keep_serialized(proof.as_slice(), platform_version)
3567 }
3568
3569 #[cfg(feature = "server")]
3570 pub fn execute_raw_results_no_proof(
3572 &self,
3573 drive: &Drive,
3574 block_info: Option<BlockInfo>,
3575 transaction: TransactionArg,
3576 platform_version: &PlatformVersion,
3577 ) -> Result<(Vec<Vec<u8>>, u16, u64), Error> {
3578 self.ensure_no_sub_queries("execute_raw_results_no_proof")?;
3579 let mut drive_operations = vec![];
3580 let (items, skipped) = self.execute_raw_results_no_proof_internal(
3581 drive,
3582 transaction,
3583 &mut drive_operations,
3584 platform_version,
3585 )?;
3586 let cost = if let Some(block_info) = block_info {
3587 let fee_result = Drive::calculate_fee(
3588 None,
3589 Some(drive_operations),
3590 &block_info.epoch,
3591 drive.config.epochs_per_era,
3592 platform_version,
3593 None,
3594 )?;
3595 fee_result.processing_fee
3596 } else {
3597 0
3598 };
3599 Ok((items, skipped, cost))
3600 }
3601
3602 #[cfg(feature = "server")]
3603 pub(crate) fn refuse_an_uncovered_index_only_projection(
3619 &self,
3620 platform_version: &PlatformVersion,
3621 ) -> Result<(), Error> {
3622 if !self.document_type.index_only() {
3623 return Ok(());
3624 }
3625 if (self.is_for_primary_key() && !self.index_only_flat_scan_applies())
3632 || self.start_at.is_some()
3633 {
3634 return Ok(());
3635 }
3636 let index = self.index_only_query_index(platform_version)?;
3637 let covers_every_property = self
3638 .document_type
3639 .flattened_properties()
3640 .iter()
3641 .filter(|(_, property)| {
3642 !matches!(property.property_type, DocumentPropertyType::Object(_))
3643 })
3644 .all(|(name, _)| {
3645 index.terminal_contains(name)
3646 || self.document_type.entry_payload().contains(name.as_str())
3647 || index
3648 .properties
3649 .iter()
3650 .any(|index_property| index_property.name == *name)
3651 });
3652 let covers_required_system_properties = self
3658 .document_type
3659 .required_fields()
3660 .iter()
3661 .filter(|name| {
3662 name.starts_with('$')
3663 && name.as_str() != document::property_names::ID
3664 && name.as_str() != document::property_names::OWNER_ID
3665 })
3666 .all(|name| {
3667 name.as_str() == document::property_names::CREATED_AT
3668 && index.involves(document::property_names::CREATED_AT)
3669 });
3670 if !covers_required_system_properties {
3671 return Err(Error::Query(uncovered_required_property_refusal()));
3672 }
3673 if covers_every_property {
3674 return Ok(());
3675 }
3676 Err(Error::Query(QuerySyntaxError::Unsupported(
3677 "this indexOnly query's index does not cover every property, so the documents it \
3678 synthesizes cannot be serialized into a non-proof response; query through an \
3679 index covering all properties, or use a proved query"
3680 .to_string(),
3681 )))
3682 }
3683
3684 #[cfg(feature = "server")]
3685 pub(crate) fn execute_raw_results_no_proof_internal(
3687 &self,
3688 drive: &Drive,
3689 transaction: TransactionArg,
3690 drive_operations: &mut Vec<LowLevelDriveOperation>,
3691 platform_version: &PlatformVersion,
3692 ) -> Result<(Vec<Vec<u8>>, u16), Error> {
3693 {
3700 use dpp::data_contract::document_type::accessors::DocumentTypeV2Getters;
3701 if self.document_type.index_only() {
3702 self.refuse_an_uncovered_index_only_projection(platform_version)?;
3703 let (documents, skipped) = self.execute_index_only_documents_no_proof_internal(
3704 drive,
3705 transaction,
3706 drive_operations,
3707 platform_version,
3708 )?;
3709 let serialized = documents
3710 .into_iter()
3711 .map(|document| {
3712 document
3713 .serialize(self.document_type, self.contract, platform_version)
3714 .map_err(|error| match index_only_serialization_refusal(&error) {
3715 Some(refusal) => Error::Query(refusal),
3716 None => error.into(),
3717 })
3718 })
3719 .collect::<Result<Vec<_>, Error>>()?;
3720 return Ok((serialized, skipped));
3721 }
3722 }
3723
3724 let path_query = self.construct_path_query_operations(
3725 drive,
3726 false,
3727 transaction,
3728 drive_operations,
3729 platform_version,
3730 )?;
3731
3732 let query_result = drive.grove_get_path_query_serialized_results(
3733 &path_query,
3734 transaction,
3735 drive_operations,
3736 &platform_version.drive,
3737 );
3738 match query_result {
3739 Err(Error::GroveDB(e))
3740 if matches!(
3741 e.as_ref(),
3742 GroveError::PathKeyNotFound(_)
3743 | GroveError::PathNotFound(_)
3744 | GroveError::PathParentLayerNotFound(_)
3745 ) =>
3746 {
3747 Ok((Vec::new(), 0))
3748 }
3749 _ => {
3750 let (data, skipped) = query_result?;
3751 let (data, cursor_skipped) = self.strip_cursor_from_page(data, platform_version)?;
3752 Ok((data, skipped.saturating_add(cursor_skipped)))
3753 }
3754 }
3755 }
3756
3757 #[cfg(feature = "server")]
3758 pub(crate) fn execute_no_proof_internal(
3760 &self,
3761 drive: &Drive,
3762 result_type: QueryResultType,
3763 transaction: TransactionArg,
3764 drive_operations: &mut Vec<LowLevelDriveOperation>,
3765 platform_version: &PlatformVersion,
3766 ) -> Result<(QueryResultElements, u16), Error> {
3767 let path_query = self.construct_path_query_operations(
3768 drive,
3769 false,
3770 transaction,
3771 drive_operations,
3772 platform_version,
3773 )?;
3774 let query_result = drive.grove_get_path_query(
3775 &path_query,
3776 transaction,
3777 result_type,
3778 drive_operations,
3779 &platform_version.drive,
3780 );
3781 match query_result {
3782 Err(Error::GroveDB(e))
3783 if matches!(
3784 e.as_ref(),
3785 GroveError::PathKeyNotFound(_)
3786 | GroveError::PathNotFound(_)
3787 | GroveError::PathParentLayerNotFound(_)
3788 ) =>
3789 {
3790 Ok((QueryResultElements::new(), 0))
3791 }
3792 _ => {
3793 let (data, skipped) = query_result?;
3794 let (data, cursor_skipped) =
3795 self.strip_cursor_from_elements(data, platform_version)?;
3796 Ok((data, skipped.saturating_add(cursor_skipped)))
3797 }
3798 }
3799 }
3800}
3801
3802impl<'a> From<&DriveDocumentQuery<'a>> for BTreeMap<String, Value> {
3804 fn from(query: &DriveDocumentQuery<'a>) -> Self {
3805 let mut response = BTreeMap::<String, Value>::new();
3806
3807 response.insert(
3810 "contract_id".to_string(),
3811 Value::Identifier(query.contract.id().to_buffer()),
3812 );
3813
3814 response.insert(
3817 "document_type_name".to_string(),
3818 Value::Text(query.document_type.name().to_string()),
3819 );
3820
3821 let all_where_clauses: Vec<WhereClause> = query.internal_clauses.clone().into();
3823 response.insert(
3824 "where".to_string(),
3825 Value::Array(all_where_clauses.into_iter().map(|v| v.into()).collect()),
3826 );
3827
3828 if let Some(offset) = query.offset {
3830 response.insert("offset".to_string(), Value::U16(offset));
3831 };
3832 if let Some(limit) = query.limit {
3834 response.insert("limit".to_string(), Value::U16(limit));
3835 };
3836 let order_by = &query.order_by;
3838 let value: Vec<Value> = order_by
3839 .into_iter()
3840 .map(|(_k, v)| v.clone().into())
3841 .collect();
3842 response.insert("orderBy".to_string(), Value::Array(value));
3843
3844 if let Some(start_at) = query.start_at {
3846 let v = Value::Identifier(start_at);
3847 if query.start_at_included {
3848 response.insert("startAt".to_string(), v);
3849 } else {
3850 response.insert("startAfter".to_string(), v);
3851 }
3852 };
3853
3854 if let Some(block_time_ms) = query.block_time_ms {
3856 response.insert("blockTime".to_string(), Value::U64(block_time_ms));
3857 };
3858
3859 response
3860 }
3861}
3862
3863#[cfg(feature = "server")]
3864#[cfg(test)]
3865mod tests {
3866
3867 use dpp::data_contract::document_type::accessors::DocumentTypeV0Getters;
3868
3869 use dpp::prelude::Identifier;
3870 use grovedb::Query;
3871 use indexmap::IndexMap;
3872 use rand::prelude::StdRng;
3873 use rand::SeedableRng;
3874 use serde_json::json;
3875 use std::borrow::Cow;
3876 use std::collections::BTreeMap;
3877 use std::option::Option::None;
3878
3879 use crate::drive::Drive;
3880 use crate::query::{
3881 DriveDocumentQuery, InternalClauses, OrderClause, WhereClause, WhereOperator,
3882 };
3883 use crate::util::storage_flags::StorageFlags;
3884
3885 use dpp::data_contract::DataContract;
3886
3887 use serde_json::Value::Null;
3888
3889 use crate::config::DriveConfig;
3890 use crate::util::test_helpers::setup::{setup_drive, setup_drive_with_initial_state_structure};
3891 use dpp::block::block_info::BlockInfo;
3892 use dpp::data_contract::accessors::v0::DataContractV0Getters;
3893 use dpp::data_contracts::SystemDataContract;
3894 use dpp::document::DocumentV0;
3895 use dpp::platform_value::string_encoding::Encoding;
3896 use dpp::platform_value::Value;
3897 use dpp::system_data_contracts::load_system_data_contract;
3898 use dpp::tests::fixtures::{get_data_contract_fixture, get_dpns_data_contract_fixture};
3899 use dpp::tests::json_document::json_document_to_contract;
3900 use dpp::util::cbor_serializer;
3901 use dpp::version::PlatformVersion;
3902
3903 fn setup_family_contract() -> (Drive, DataContract) {
3904 let platform_version = PlatformVersion::latest();
3905
3906 let drive = setup_drive(None);
3907
3908 drive
3909 .create_initial_state_structure(None, platform_version)
3910 .expect("expected to create root tree successfully");
3911
3912 let contract_path = "tests/supporting_files/contract/family/family-contract.json";
3913
3914 let contract = json_document_to_contract(contract_path, false, platform_version)
3916 .expect("expected to get document");
3917
3918 let storage_flags = Some(Cow::Owned(StorageFlags::SingleEpoch(0)));
3919 drive
3920 .apply_contract(
3921 &contract,
3922 BlockInfo::default(),
3923 true,
3924 storage_flags,
3925 None,
3926 platform_version,
3927 )
3928 .expect("expected to apply contract successfully");
3929
3930 (drive, contract)
3931 }
3932
3933 fn setup_withdrawal_contract() -> (Drive, DataContract) {
3934 let platform_version = PlatformVersion::latest();
3935
3936 let drive = setup_drive(None);
3937
3938 drive
3939 .create_initial_state_structure(None, platform_version)
3940 .expect("expected to create root tree successfully");
3941
3942 let contract = load_system_data_contract(SystemDataContract::Withdrawals, platform_version)
3944 .expect("load system contact");
3945
3946 let storage_flags = Some(Cow::Owned(StorageFlags::SingleEpoch(0)));
3947 drive
3948 .apply_contract(
3949 &contract,
3950 BlockInfo::default(),
3951 true,
3952 storage_flags,
3953 None,
3954 platform_version,
3955 )
3956 .expect("expected to apply contract successfully");
3957
3958 (drive, contract)
3959 }
3960
3961 fn setup_family_birthday_contract() -> (Drive, DataContract) {
3962 let drive = setup_drive_with_initial_state_structure(None);
3963
3964 let platform_version = PlatformVersion::latest();
3965
3966 let contract_path =
3967 "tests/supporting_files/contract/family/family-contract-with-birthday.json";
3968
3969 let contract = json_document_to_contract(contract_path, false, platform_version)
3971 .expect("expected to get document");
3972 let storage_flags = Some(Cow::Owned(StorageFlags::SingleEpoch(0)));
3973 drive
3974 .apply_contract(
3975 &contract,
3976 BlockInfo::default(),
3977 true,
3978 storage_flags,
3979 None,
3980 platform_version,
3981 )
3982 .expect("expected to apply contract successfully");
3983
3984 (drive, contract)
3985 }
3986
3987 #[test]
3988 fn test_drive_query_from_to_cbor() {
3989 let config = DriveConfig::default();
3990 let contract = get_data_contract_fixture(None, 0, 1).data_contract_owned();
3991 let document_type = contract
3992 .document_type_for_name("niceDocument")
3993 .expect("expected to get nice document");
3994 let start_after = Identifier::random();
3995
3996 let query_value = json!({
3997 "contract_id": contract.id(),
3998 "document_type_name": document_type.name(),
3999 "where": [
4000 ["firstName", "<", "Gilligan"],
4001 ["lastName", "=", "Doe"]
4002 ],
4003 "limit": 100u16,
4004 "offset": 10u16,
4005 "orderBy": [
4006 ["firstName", "asc"],
4007 ["lastName", "desc"],
4008 ],
4009 "startAfter": start_after,
4010 "blockTime": 13453432u64,
4011 });
4012
4013 let where_cbor = cbor_serializer::serializable_value_to_cbor(&query_value, None)
4014 .expect("expected to serialize to cbor");
4015 let query = DriveDocumentQuery::from_cbor(
4016 where_cbor.as_slice(),
4017 &contract,
4018 document_type,
4019 &config,
4020 PlatformVersion::latest(),
4021 )
4022 .expect("deserialize cbor shouldn't fail");
4023
4024 let cbor = query.to_cbor().expect("should serialize cbor");
4025
4026 let deserialized = DriveDocumentQuery::from_cbor(
4027 &cbor,
4028 &contract,
4029 document_type,
4030 &config,
4031 PlatformVersion::latest(),
4032 )
4033 .expect("should deserialize cbor");
4034
4035 assert_eq!(query, deserialized);
4036
4037 assert_eq!(deserialized.start_at, Some(start_after.to_buffer()));
4038 assert!(!deserialized.start_at_included);
4039 assert_eq!(deserialized.block_time_ms, Some(13453432u64));
4040 }
4041
4042 #[test]
4043 fn test_invalid_query_ranges_different_fields() {
4044 let query_value = json!({
4045 "where": [
4046 ["firstName", "<", "Gilligan"],
4047 ["lastName", "<", "Michelle"],
4048 ],
4049 "limit": 100,
4050 "orderBy": [
4051 ["firstName", "asc"],
4052 ["lastName", "asc"],
4053 ]
4054 });
4055 let contract = get_data_contract_fixture(None, 0, 1).data_contract_owned();
4056 let document_type = contract
4057 .document_type_for_name("niceDocument")
4058 .expect("expected to get nice document");
4059
4060 let where_cbor = cbor_serializer::serializable_value_to_cbor(&query_value, None)
4061 .expect("expected to serialize to cbor");
4062 DriveDocumentQuery::from_cbor(
4063 where_cbor.as_slice(),
4064 &contract,
4065 document_type,
4066 &DriveConfig::default(),
4067 PlatformVersion::latest(),
4068 )
4069 .expect_err("all ranges must be on same field");
4070 }
4071
4072 #[test]
4073 fn test_invalid_query_extra_invalid_field() {
4074 let query_value = json!({
4075 "where": [
4076 ["firstName", "<", "Gilligan"],
4077 ],
4078 "limit": 100,
4079 "orderBy": [
4080 ["firstName", "asc"],
4081 ["lastName", "asc"],
4082 ],
4083 "invalid": 0,
4084 });
4085 let contract = get_data_contract_fixture(None, 0, 1).data_contract_owned();
4086 let document_type = contract
4087 .document_type_for_name("niceDocument")
4088 .expect("expected to get nice document");
4089
4090 let where_cbor = cbor_serializer::serializable_value_to_cbor(&query_value, None)
4091 .expect("expected to serialize to cbor");
4092 DriveDocumentQuery::from_cbor(
4093 where_cbor.as_slice(),
4094 &contract,
4095 document_type,
4096 &DriveConfig::default(),
4097 PlatformVersion::latest(),
4098 )
4099 .expect_err("fields of queries must of defined supported types (where, limit, orderBy...)");
4100 }
4101
4102 #[test]
4103 fn test_invalid_query_conflicting_clauses() {
4104 let query_value = json!({
4105 "where": [
4106 ["firstName", "<", "Gilligan"],
4107 ["firstName", ">", "Gilligan"],
4108 ],
4109 "limit": 100,
4110 "orderBy": [
4111 ["firstName", "asc"],
4112 ["lastName", "asc"],
4113 ],
4114 });
4115
4116 let contract = get_data_contract_fixture(None, 0, 1).data_contract_owned();
4117 let document_type = contract
4118 .document_type_for_name("niceDocument")
4119 .expect("expected to get nice document");
4120
4121 let where_cbor = cbor_serializer::serializable_value_to_cbor(&query_value, None)
4122 .expect("expected to serialize to cbor");
4123 DriveDocumentQuery::from_cbor(
4124 where_cbor.as_slice(),
4125 &contract,
4126 document_type,
4127 &DriveConfig::default(),
4128 PlatformVersion::latest(),
4129 )
4130 .expect_err("the query should not be created");
4131 }
4132
4133 #[test]
4134 fn test_valid_query_groupable_meeting_clauses() {
4135 let query_value = json!({
4136 "where": [
4137 ["firstName", "<=", "Gilligan"],
4138 ["firstName", ">", "Gilligan"],
4139 ],
4140 "limit": 100,
4141 "orderBy": [
4142 ["firstName", "asc"],
4143 ["lastName", "asc"],
4144 ],
4145 });
4146
4147 let contract = get_data_contract_fixture(None, 0, 1).data_contract_owned();
4148 let document_type = contract
4149 .document_type_for_name("niceDocument")
4150 .expect("expected to get nice document");
4151
4152 let where_cbor = cbor_serializer::serializable_value_to_cbor(&query_value, None)
4153 .expect("expected to serialize to cbor");
4154 DriveDocumentQuery::from_cbor(
4155 where_cbor.as_slice(),
4156 &contract,
4157 document_type,
4158 &DriveConfig::default(),
4159 PlatformVersion::latest(),
4160 )
4161 .expect("the query should be created");
4162 }
4163
4164 #[test]
4165 fn test_valid_query_query_field_at_max_length() {
4166 let long_string = "t".repeat(255);
4167 let query_value = json!({
4168 "where": [
4169 ["firstName", "<", long_string],
4170 ],
4171 "limit": 100,
4172 "orderBy": [
4173 ["firstName", "asc"],
4174 ["lastName", "asc"],
4175 ],
4176 });
4177 let contract = get_data_contract_fixture(None, 0, 1).data_contract_owned();
4178 let document_type = contract
4179 .document_type_for_name("niceDocument")
4180 .expect("expected to get nice document");
4181
4182 let where_cbor = cbor_serializer::serializable_value_to_cbor(&query_value, None)
4183 .expect("expected to serialize to cbor");
4184 DriveDocumentQuery::from_cbor(
4185 where_cbor.as_slice(),
4186 &contract,
4187 document_type,
4188 &DriveConfig::default(),
4189 PlatformVersion::latest(),
4190 )
4191 .expect("query should be fine for a 255 byte long string");
4192 }
4193
4194 #[test]
4195 fn test_valid_query_drive_document_query() {
4196 let platform_version = PlatformVersion::latest();
4197 let mut rng = StdRng::seed_from_u64(5);
4198 let contract =
4199 get_dpns_data_contract_fixture(Some(Identifier::random_with_rng(&mut rng)), 0, 1)
4200 .data_contract_owned();
4201 let domain = contract
4202 .document_type_for_name("domain")
4203 .expect("expected to get domain");
4204
4205 let query_asc = DriveDocumentQuery {
4206 contract: &contract,
4207 document_type: domain,
4208 internal_clauses: InternalClauses {
4209 primary_key_in_clause: None,
4210 primary_key_equal_clause: None,
4211 in_clauses: Vec::new(),
4212 range_clause: Some(WhereClause {
4213 field: "records.identity".to_string(),
4214 operator: WhereOperator::LessThan,
4215 value: Value::Identifier(
4216 Identifier::from_string(
4217 "AYN4srupPWDrp833iG5qtmaAsbapNvaV7svAdncLN5Rh",
4218 Encoding::Base58,
4219 )
4220 .unwrap()
4221 .to_buffer(),
4222 ),
4223 }),
4224 equal_clauses: BTreeMap::new(),
4225 },
4226 offset: None,
4227 limit: Some(6),
4228 order_by: vec![(
4229 "records.identity".to_string(),
4230 OrderClause {
4231 field: "records.identity".to_string(),
4232 ascending: false,
4233 },
4234 )]
4235 .into_iter()
4236 .collect(),
4237 start_at: None,
4238 start_at_included: false,
4239 block_time_ms: None,
4240 resolved_time_ranges: vec![],
4241 sub_queries: vec![],
4242 };
4243
4244 let path_query = query_asc
4245 .construct_path_query(None, platform_version)
4246 .expect("expected to create path query");
4247
4248 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 } }");
4249
4250 let encoded = bincode::encode_to_vec(&path_query, bincode::config::standard())
4252 .expect("Failed to serialize PathQuery");
4253
4254 let hex_string = hex::encode(encoded);
4256
4257 assert_eq!(hex_string, "050140201da29f488023e306ff9a680bc9837153fb0778c8ee9c934a87dc0de1d69abd3c010106646f6d61696e107265636f7264732e6964656e74697479010105208dc201fd7ad7905f8a84d66218e2b387daea7fe4739ae0e21e8c3ee755e6a2c00101010001010103000000000001010000010101000101010300000000000000010600");
4261 }
4262
4263 #[test]
4264 fn test_invalid_query_field_too_long() {
4265 let (drive, contract) = setup_family_contract();
4266
4267 let platform_version = PlatformVersion::latest();
4268
4269 let document_type = contract
4270 .document_type_for_name("person")
4271 .expect("expected to get a document type");
4272
4273 let too_long_string = "t".repeat(256);
4274 let query_value = json!({
4275 "where": [
4276 ["firstName", "<", too_long_string],
4277 ],
4278 "limit": 100,
4279 "orderBy": [
4280 ["firstName", "asc"],
4281 ["lastName", "asc"],
4282 ],
4283 });
4284
4285 let where_cbor = cbor_serializer::serializable_value_to_cbor(&query_value, None)
4286 .expect("expected to serialize to cbor");
4287 let query = DriveDocumentQuery::from_cbor(
4288 where_cbor.as_slice(),
4289 &contract,
4290 document_type,
4291 &DriveConfig::default(),
4292 PlatformVersion::latest(),
4293 )
4294 .expect("fields of queries length must be under 256 bytes long");
4295 query
4296 .execute_raw_results_no_proof(&drive, None, None, platform_version)
4297 .expect_err("fields of queries length must be under 256 bytes long");
4298 }
4299
4300 #[test]
4356 fn test_valid_query_timestamp_field_with_null_value() {
4357 let (drive, contract) = setup_family_birthday_contract();
4358
4359 let platform_version = PlatformVersion::latest();
4360
4361 let document_type = contract
4362 .document_type_for_name("person")
4363 .expect("expected to get a document type");
4364
4365 let query_value = json!({
4366 "where": [
4367 ["birthday", ">=", Null],
4368 ],
4369 "limit": 100,
4370 "orderBy": [
4371 ["birthday", "asc"],
4372 ],
4373 });
4374
4375 let where_cbor = cbor_serializer::serializable_value_to_cbor(&query_value, None)
4376 .expect("expected to serialize to cbor");
4377 let query = DriveDocumentQuery::from_cbor(
4378 where_cbor.as_slice(),
4379 &contract,
4380 document_type,
4381 &DriveConfig::default(),
4382 PlatformVersion::latest(),
4383 )
4384 .expect("The query itself should be valid for a null type");
4385 query
4386 .execute_raw_results_no_proof(&drive, None, None, platform_version)
4387 .expect("a Null value doesn't make sense for a float");
4388 }
4389
4390 #[test]
4391 fn test_invalid_query_in_with_empty_array() {
4392 let (drive, contract) = setup_family_contract();
4393
4394 let platform_version = PlatformVersion::latest();
4395
4396 let document_type = contract
4397 .document_type_for_name("person")
4398 .expect("expected to get a document type");
4399
4400 let query_value = json!({
4401 "where": [
4402 ["firstName", "in", []],
4403 ],
4404 "limit": 100,
4405 "orderBy": [
4406 ["firstName", "asc"],
4407 ],
4408 });
4409
4410 let where_cbor = cbor_serializer::serializable_value_to_cbor(&query_value, None)
4411 .expect("expected to serialize to cbor");
4412 let query = DriveDocumentQuery::from_cbor(
4413 where_cbor.as_slice(),
4414 &contract,
4415 document_type,
4416 &DriveConfig::default(),
4417 PlatformVersion::latest(),
4418 )
4419 .expect("query should be valid for empty array");
4420
4421 query
4422 .execute_raw_results_no_proof(&drive, None, None, platform_version)
4423 .expect_err("query should not be able to execute for empty array");
4424 }
4425
4426 #[test]
4427 fn test_invalid_query_in_too_many_elements() {
4428 let (drive, contract) = setup_family_contract();
4429
4430 let platform_version = PlatformVersion::latest();
4431
4432 let document_type = contract
4433 .document_type_for_name("person")
4434 .expect("expected to get a document type");
4435
4436 let mut array: Vec<String> = Vec::with_capacity(101);
4437 for _ in 0..array.capacity() {
4438 array.push(String::from("a"));
4439 }
4440 let query_value = json!({
4441 "where": [
4442 ["firstName", "in", array],
4443 ],
4444 "limit": 100,
4445 "orderBy": [
4446 ["firstName", "asc"],
4447 ],
4448 });
4449
4450 let where_cbor = cbor_serializer::serializable_value_to_cbor(&query_value, None)
4451 .expect("expected to serialize to cbor");
4452 let query = DriveDocumentQuery::from_cbor(
4453 where_cbor.as_slice(),
4454 &contract,
4455 document_type,
4456 &DriveConfig::default(),
4457 PlatformVersion::latest(),
4458 )
4459 .expect("query is valid for too many elements");
4460
4461 query
4462 .execute_raw_results_no_proof(&drive, None, None, platform_version)
4463 .expect_err("query should not be able to execute with too many elements");
4464 }
4465
4466 #[test]
4467 fn test_invalid_query_in_unique_elements() {
4468 let (drive, contract) = setup_family_contract();
4469
4470 let platform_version = PlatformVersion::latest();
4471
4472 let document_type = contract
4473 .document_type_for_name("person")
4474 .expect("expected to get a document type");
4475
4476 let query_value = json!({
4477 "where": [
4478 ["firstName", "in", ["a", "a"]],
4479 ],
4480 "limit": 100,
4481 "orderBy": [
4482 ["firstName", "asc"],
4483 ],
4484 });
4485
4486 let where_cbor = cbor_serializer::serializable_value_to_cbor(&query_value, None)
4487 .expect("expected to serialize to cbor");
4488
4489 let query = DriveDocumentQuery::from_cbor(
4493 where_cbor.as_slice(),
4494 &contract,
4495 document_type,
4496 &DriveConfig::default(),
4497 PlatformVersion::latest(),
4498 )
4499 .expect("the query should be created");
4500
4501 query
4502 .execute_raw_results_no_proof(&drive, None, None, platform_version)
4503 .expect_err("there should be no duplicates values for In query");
4504 }
4505
4506 #[test]
4507 fn test_invalid_query_starts_with_empty_string() {
4508 let query_value = json!({
4509 "where": [
4510 ["firstName", "startsWith", ""],
4511 ],
4512 "limit": 100,
4513 "orderBy": [
4514 ["firstName", "asc"],
4515 ],
4516 });
4517
4518 let contract = get_data_contract_fixture(None, 0, 1).data_contract_owned();
4519 let document_type = contract
4520 .document_type_for_name("niceDocument")
4521 .expect("expected to get nice document");
4522
4523 let where_cbor = cbor_serializer::serializable_value_to_cbor(&query_value, None)
4524 .expect("expected to serialize to cbor");
4525 DriveDocumentQuery::from_cbor(
4526 where_cbor.as_slice(),
4527 &contract,
4528 document_type,
4529 &DriveConfig::default(),
4530 PlatformVersion::latest(),
4531 )
4532 .expect_err("starts with can not start with an empty string");
4533 }
4534
4535 #[test]
4536 fn test_invalid_query_limit_too_high() {
4537 let query_value = json!({
4538 "where": [
4539 ["firstName", "startsWith", "a"],
4540 ],
4541 "limit": 101,
4542 "orderBy": [
4543 ["firstName", "asc"],
4544 ],
4545 });
4546
4547 let contract = get_data_contract_fixture(None, 0, 1).data_contract_owned();
4548 let document_type = contract
4549 .document_type_for_name("niceDocument")
4550 .expect("expected to get nice document");
4551
4552 let where_cbor = cbor_serializer::serializable_value_to_cbor(&query_value, None)
4553 .expect("expected to serialize to cbor");
4554 DriveDocumentQuery::from_cbor(
4555 where_cbor.as_slice(),
4556 &contract,
4557 document_type,
4558 &DriveConfig::default(),
4559 PlatformVersion::latest(),
4560 )
4561 .expect_err("starts with can not start with an empty string");
4562 }
4563
4564 #[test]
4565 fn test_invalid_query_limit_too_low() {
4566 let query_value = json!({
4567 "where": [
4568 ["firstName", "startsWith", "a"],
4569 ],
4570 "limit": -1,
4571 "orderBy": [
4572 ["firstName", "asc"],
4573 ],
4574 });
4575
4576 let contract = get_data_contract_fixture(None, 0, 1).data_contract_owned();
4577 let document_type = contract
4578 .document_type_for_name("niceDocument")
4579 .expect("expected to get nice document");
4580
4581 let where_cbor = cbor_serializer::serializable_value_to_cbor(&query_value, None)
4582 .expect("expected to serialize to cbor");
4583 DriveDocumentQuery::from_cbor(
4584 where_cbor.as_slice(),
4585 &contract,
4586 document_type,
4587 &DriveConfig::default(),
4588 PlatformVersion::latest(),
4589 )
4590 .expect_err("starts with can not start with an empty string");
4591 }
4592
4593 #[test]
4594 fn test_invalid_query_limit_zero() {
4595 let query_value = json!({
4596 "where": [
4597 ["firstName", "startsWith", "a"],
4598 ],
4599 "limit": 0,
4600 "orderBy": [
4601 ["firstName", "asc"],
4602 ],
4603 });
4604
4605 let contract = get_data_contract_fixture(None, 0, 1).data_contract_owned();
4606 let document_type = contract
4607 .document_type_for_name("niceDocument")
4608 .expect("expected to get nice document");
4609
4610 let where_cbor = cbor_serializer::serializable_value_to_cbor(&query_value, None)
4611 .expect("expected to serialize to cbor");
4612 DriveDocumentQuery::from_cbor(
4613 where_cbor.as_slice(),
4614 &contract,
4615 document_type,
4616 &DriveConfig::default(),
4617 PlatformVersion::latest(),
4618 )
4619 .expect_err("starts with can not start with an empty string");
4620 }
4621
4622 #[test]
4623 fn resolved_time_range_shape_guard_accepts_only_the_single_resolution_equality() {
4624 use crate::query::{validate_resolved_time_range_clause_shapes, ResolvedTimeRange};
4625 use dpp::data_contract::document_type::TimeRangeTransform;
4626
4627 let resolved = vec![ResolvedTimeRange {
4628 transform: TimeRangeTransform {
4629 source: "$createdAt".to_string(),
4630 range_seconds: 21_600,
4631 step_seconds: 7_200,
4632 phase_seconds: 0,
4633 ttl_seconds: None,
4634 }
4635 .into(),
4636 }];
4637 let equality = WhereClause {
4638 field: "$createdAt".to_string(),
4639 operator: WhereOperator::Equal,
4640 value: Value::U64(21_600_000),
4641 };
4642 let other = WhereClause {
4643 field: "hashtag".to_string(),
4644 operator: WhereOperator::Equal,
4645 value: Value::Text("ibiza".to_string()),
4646 };
4647
4648 validate_resolved_time_range_clause_shapes(&[equality.clone(), other.clone()], &resolved)
4649 .expect("one equality on the resolved field is the resolution shape");
4650
4651 let in_clause = WhereClause {
4654 field: "$createdAt".to_string(),
4655 operator: WhereOperator::In,
4656 value: Value::Array(vec![Value::U64(0), Value::U64(7_200_000)]),
4657 };
4658 validate_resolved_time_range_clause_shapes(&[in_clause, other.clone()], &resolved)
4659 .expect_err("an In clause on a resolved field must be rejected");
4660
4661 let range_clause = WhereClause {
4662 field: "$createdAt".to_string(),
4663 operator: WhereOperator::GreaterThan,
4664 value: Value::U64(0),
4665 };
4666 validate_resolved_time_range_clause_shapes(&[equality.clone(), range_clause], &resolved)
4667 .expect_err("a range clause riding along on a resolved field must be rejected");
4668
4669 validate_resolved_time_range_clause_shapes(&[other], &resolved)
4670 .expect_err("a resolved field with no equality at all must be rejected");
4671 }
4672
4673 #[test]
4679 fn by_start_rejects_windows_past_the_ttl_horizon() {
4680 use crate::query::{resolve_time_range_bucket_clause, TimeRangeSelector};
4681 use dpp::data_contract::DataContractFactory;
4682 use dpp::platform_value::platform_value;
4683 use dpp::prelude::Identifier;
4684
4685 let factory =
4686 DataContractFactory::new(PlatformVersion::latest().protocol_version).expect("factory");
4687 let hour_ms: u64 = 3_600_000;
4688 let build = |seed: u8, with_ttl: bool| {
4689 let mut time_range = vec![
4690 (
4691 Value::Text("on".to_string()),
4692 Value::Text("$createdAt".to_string()),
4693 ),
4694 (Value::Text("range".to_string()), Value::U64(7_200)),
4695 (Value::Text("step".to_string()), Value::U64(7_200)),
4696 ];
4697 if with_ttl {
4698 time_range.push((Value::Text("ttl".to_string()), Value::U64(14_400)));
4699 }
4700 let index_map = vec![
4701 (
4702 Value::Text("name".to_string()),
4703 Value::Text("trending".to_string()),
4704 ),
4705 (
4706 Value::Text("properties".to_string()),
4707 Value::Array(vec![
4708 platform_value!({"$createdAt": "asc"}),
4709 platform_value!({"hashtag": "asc"}),
4710 ]),
4711 ),
4712 (Value::Text("timeRange".to_string()), Value::Map(time_range)),
4713 (
4714 Value::Text("countable".to_string()),
4715 Value::Text("countable".to_string()),
4716 ),
4717 ];
4718 let document_schema = platform_value!({
4719 "type": "object",
4720 "properties": {
4721 "hashtag": {"type": "string", "maxLength": 61, "position": 0},
4722 },
4723 "required": ["hashtag", "$createdAt"],
4724 "indices": Value::Array(vec![Value::Map(index_map)]),
4725 "additionalProperties": false,
4726 });
4727 factory
4728 .create_with_value_config(
4729 Identifier::from([seed; 32]),
4730 0,
4731 platform_value!({ "post": document_schema }),
4732 None,
4733 None,
4734 )
4735 .expect("contract registers")
4736 .data_contract_owned()
4737 };
4738
4739 let ttl_contract = build(101, true);
4740 let standing_contract = build(102, false);
4741 let expired_start = 5_000 * hour_ms;
4742 let block_time = expired_start + 6 * hour_ms;
4743
4744 let resolve = |contract: &DataContract, start_ms: u64| {
4745 resolve_time_range_bucket_clause(
4746 "$createdAt",
4747 TimeRangeSelector::ByStart { start_ms },
4748 None,
4749 contract
4750 .document_type_for_name("post")
4751 .expect("document type"),
4752 block_time,
4753 )
4754 };
4755
4756 let error = resolve(&ttl_contract, expired_start)
4757 .expect_err("a window past the ttl horizon must be rejected, not served");
4758 assert!(
4759 error.to_string().contains("ttl horizon"),
4760 "the rejection names the horizon: {error}"
4761 );
4762 resolve(&ttl_contract, expired_start + 2 * hour_ms).expect(
4763 "a window starting exactly at the horizon is not expired — same \
4764 strictly-below boundary the drain uses",
4765 );
4766 resolve(&ttl_contract, expired_start + 4 * hour_ms)
4767 .expect("a live window resolves normally");
4768 resolve_time_range_bucket_clause(
4769 "$createdAt",
4770 TimeRangeSelector::Newest,
4771 None,
4772 ttl_contract
4773 .document_type_for_name("post")
4774 .expect("document type"),
4775 block_time,
4776 )
4777 .expect("relative selectors never address expired windows and stay unaffected");
4778 resolve(&standing_contract, expired_start)
4779 .expect("without a ttl, arbitrarily old windows stay queryable");
4780 }
4781
4782 #[test]
4783 fn test_withdrawal_query_with_missing_transaction_index() {
4784 let (_, contract) = setup_withdrawal_contract();
4786 let platform_version = PlatformVersion::latest();
4787
4788 let document_type_name = "withdrawal";
4789 let document_type = contract
4790 .document_type_for_name(document_type_name)
4791 .expect("expected to get document type");
4792
4793 let drive_document_query = DriveDocumentQuery {
4795 contract: &contract,
4796 document_type,
4797 internal_clauses: InternalClauses {
4798 primary_key_in_clause: None,
4799 primary_key_equal_clause: None,
4800 in_clauses: vec![WhereClause {
4801 field: "status".to_string(),
4802 operator: WhereOperator::In,
4803 value: Value::Array(vec![
4804 Value::U64(0),
4805 Value::U64(1),
4806 Value::U64(2),
4807 Value::U64(3),
4808 Value::U64(4),
4809 ]),
4810 }],
4811 range_clause: None,
4812 equal_clauses: BTreeMap::default(),
4813 },
4814 offset: None,
4815 limit: Some(3),
4816 order_by: IndexMap::from([
4817 (
4818 "status".to_string(),
4819 OrderClause {
4820 field: "status".to_string(),
4821 ascending: true,
4822 },
4823 ),
4824 (
4825 "transactionIndex".to_string(),
4826 OrderClause {
4827 field: "transactionIndex".to_string(),
4828 ascending: true,
4829 },
4830 ),
4831 ]),
4832 start_at: Some([3u8; 32]),
4833 start_at_included: false,
4834 block_time_ms: None,
4835 resolved_time_ranges: vec![],
4836 sub_queries: vec![],
4837 };
4838
4839 let mut properties = BTreeMap::new();
4841 properties.insert("status".to_string(), Value::U64(0));
4842 let starts_at_document = DocumentV0 {
4845 contract_version: None,
4846 id: Identifier::from([3u8; 32]), owner_id: Identifier::random(),
4848 properties,
4849 revision: None,
4850 created_at: None,
4851 updated_at: None,
4852 transferred_at: None,
4853 created_at_block_height: None,
4854 updated_at_block_height: None,
4855 transferred_at_block_height: None,
4856 created_at_core_block_height: None,
4857 updated_at_core_block_height: None,
4858 transferred_at_core_block_height: None,
4859 creator_id: None,
4860 moderated_at: None,
4861 moderated_by: None,
4862 }
4863 .into();
4864
4865 let result = drive_document_query
4867 .construct_path_query(Some(starts_at_document), platform_version)
4868 .expect("expected to construct a path query");
4869
4870 assert_eq!(
4871 result
4872 .clone()
4873 .query
4874 .query
4875 .default_subquery_branch
4876 .subquery
4877 .expect("expected subquery")
4878 .items,
4879 Query::new_range_full().items
4880 );
4881 }
4882
4883 mod multiple_in_clause_lowering {
4890 use super::*;
4891 use crate::error::query::QuerySyntaxError;
4892 use crate::error::Error;
4893
4894 fn family_contract() -> DataContract {
4895 json_document_to_contract(
4896 "tests/supporting_files/contract/family/family-contract.json",
4897 false,
4898 PlatformVersion::latest(),
4899 )
4900 .expect("expected to load family contract")
4901 }
4902
4903 fn text_array(values: &[&str]) -> Value {
4904 Value::Array(
4905 values
4906 .iter()
4907 .map(|value| Value::Text(value.to_string()))
4908 .collect(),
4909 )
4910 }
4911
4912 fn in_clause(field: &str, values: &[&str]) -> WhereClause {
4913 WhereClause {
4914 field: field.to_string(),
4915 operator: WhereOperator::In,
4916 value: text_array(values),
4917 }
4918 }
4919
4920 fn ascending_order_by(fields: &[&str]) -> IndexMap<String, OrderClause> {
4921 fields
4922 .iter()
4923 .map(|field| {
4924 (
4925 field.to_string(),
4926 OrderClause {
4927 field: field.to_string(),
4928 ascending: true,
4929 },
4930 )
4931 })
4932 .collect()
4933 }
4934
4935 fn person_query<'a>(
4936 contract: &'a DataContract,
4937 where_clauses: Vec<WhereClause>,
4938 order_by_fields: &[&str],
4939 ) -> DriveDocumentQuery<'a> {
4940 let internal_clauses =
4941 InternalClauses::extract_from_clauses(where_clauses, PlatformVersion::latest())
4942 .expect("clauses should group structurally");
4943 DriveDocumentQuery {
4944 contract,
4945 document_type: contract
4946 .document_type_for_name("person")
4947 .expect("person document type should exist"),
4948 internal_clauses,
4949 offset: None,
4950 limit: Some(100),
4951 order_by: ascending_order_by(order_by_fields),
4952 start_at: None,
4953 start_at_included: false,
4954 block_time_ms: None,
4955 resolved_time_ranges: vec![],
4956 sub_queries: vec![],
4957 }
4958 }
4959
4960 #[test]
4961 fn two_in_clauses_lower_to_nested_key_sets() {
4962 let contract = family_contract();
4963 let platform_version = PlatformVersion::latest();
4964 let query = person_query(
4965 &contract,
4966 vec![
4967 in_clause("firstName", &["Adey", "Briney"]),
4968 in_clause("lastName", &["Kriskov", "Randolf"]),
4969 ],
4970 &["firstName", "lastName"],
4971 );
4972
4973 let path_query = query
4974 .construct_path_query(None, platform_version)
4975 .expect("two in clauses should lower at protocol version 14");
4976
4977 assert_eq!(
4980 path_query.path.last().expect("path should not be empty"),
4981 &b"firstName".to_vec()
4982 );
4983
4984 let outer = &path_query.query.query;
4986 assert_eq!(outer.items.len(), 2);
4987 assert!(outer.left_to_right);
4988
4989 assert_eq!(
4992 outer.default_subquery_branch.subquery_path,
4993 Some(vec![b"lastName".to_vec()])
4994 );
4995 let inner = outer
4996 .default_subquery_branch
4997 .subquery
4998 .as_deref()
4999 .expect("expected a lastName subquery");
5000 assert_eq!(inner.items.len(), 2);
5001
5002 assert_eq!(
5004 inner.default_subquery_branch.subquery_path,
5005 Some(vec![vec![0]])
5006 );
5007 }
5008
5009 #[test]
5010 #[cfg(feature = "cbor_query")]
5011 fn two_in_clauses_survive_cbor_round_trip() {
5012 let contract = family_contract();
5013 let mut query = person_query(
5014 &contract,
5015 vec![
5016 in_clause("firstName", &["Adey", "Briney"]),
5017 in_clause("lastName", &["Kriskov", "Randolf"]),
5018 ],
5019 &["firstName", "lastName"],
5020 );
5021 query.start_at_included = true;
5024
5025 let cbor = query.to_cbor().expect("should serialize cbor");
5026 let deserialized = DriveDocumentQuery::from_cbor(
5027 &cbor,
5028 &contract,
5029 contract
5030 .document_type_for_name("person")
5031 .expect("person document type should exist"),
5032 &DriveConfig::default(),
5033 PlatformVersion::latest(),
5034 )
5035 .expect("should deserialize cbor");
5036
5037 assert_eq!(query, deserialized);
5038 assert_eq!(
5039 deserialized
5040 .internal_clauses
5041 .in_clauses
5042 .iter()
5043 .map(|in_clause| in_clause.field.as_str())
5044 .collect::<Vec<_>>(),
5045 vec!["firstName", "lastName"],
5046 "both in clauses must survive the round trip in order"
5047 );
5048 }
5049
5050 #[test]
5051 fn descending_order_by_on_left_over_property_is_honored() {
5052 let contract = family_contract();
5053 let platform_version = PlatformVersion::latest();
5054 let mut query = person_query(
5057 &contract,
5058 vec![
5059 in_clause("firstName", &["Adey", "Briney"]),
5060 in_clause("middleName", &["Ivanna", "Evangeline"]),
5061 ],
5062 &["firstName", "middleName"],
5063 );
5064 query.order_by.insert(
5065 "lastName".to_string(),
5066 OrderClause {
5067 field: "lastName".to_string(),
5068 ascending: false,
5069 },
5070 );
5071
5072 let path_query = query
5073 .construct_path_query(None, platform_version)
5074 .expect("two in clauses with a left-over order should lower");
5075
5076 let outer = &path_query.query.query;
5077 let middle = outer
5078 .default_subquery_branch
5079 .subquery
5080 .as_deref()
5081 .expect("expected a middleName subquery");
5082 assert_eq!(
5083 middle.default_subquery_branch.subquery_path,
5084 Some(vec![b"lastName".to_vec()])
5085 );
5086 let left_over_level = middle
5087 .default_subquery_branch
5088 .subquery
5089 .as_deref()
5090 .expect("expected a lastName subquery");
5091 assert!(
5092 !left_over_level.left_to_right,
5093 "left-over lastName level must honor the descending order by"
5094 );
5095
5096 query.order_by.shift_remove("lastName");
5099 let path_query = query
5100 .construct_path_query(None, platform_version)
5101 .expect("two in clauses should lower");
5102 let left_over_level = path_query
5103 .query
5104 .query
5105 .default_subquery_branch
5106 .subquery
5107 .as_deref()
5108 .expect("expected a middleName subquery")
5109 .default_subquery_branch
5110 .subquery
5111 .as_deref()
5112 .expect("expected a lastName subquery");
5113 assert!(left_over_level.left_to_right);
5114 }
5115
5116 #[test]
5117 fn two_in_clauses_rejected_at_protocol_version_13() {
5118 let contract = family_contract();
5119 let platform_version_13 =
5120 PlatformVersion::get(13).expect("protocol version 13 should exist");
5121 let query = person_query(
5122 &contract,
5123 vec![
5124 in_clause("firstName", &["Adey", "Briney"]),
5125 in_clause("lastName", &["Kriskov", "Randolf"]),
5126 ],
5127 &["firstName", "lastName"],
5128 );
5129
5130 let error = query
5131 .construct_path_query(None, platform_version_13)
5132 .expect_err("multiple in clauses must be rejected before protocol version 14");
5133 assert!(
5134 matches!(error, Error::Query(QuerySyntaxError::MultipleInClauses(_))),
5135 "expected MultipleInClauses, got {error:?}"
5136 );
5137
5138 query
5139 .construct_path_query(None, PlatformVersion::latest())
5140 .expect("the same query should lower at protocol version 14");
5141 }
5142
5143 #[test]
5144 fn equality_prefix_two_in_clauses_and_trailing_range_lowering() {
5145 let contract = family_contract();
5146 let platform_version = PlatformVersion::latest();
5147 let mut query = person_query(
5148 &contract,
5149 vec![
5150 WhereClause {
5151 field: "age".to_string(),
5152 operator: WhereOperator::Equal,
5153 value: Value::U8(30),
5154 },
5155 in_clause("firstName", &["Adey", "Briney"]),
5156 in_clause("middleName", &["Ivanna", "Evangeline"]),
5157 WhereClause {
5158 field: "lastName".to_string(),
5159 operator: WhereOperator::GreaterThan,
5160 value: Value::Text("M".to_string()),
5161 },
5162 ],
5163 &["firstName", "middleName", "lastName"],
5164 );
5165 query.limit = Some(50);
5166
5167 let path_query = query
5171 .construct_path_query(None, platform_version)
5172 .expect("equality + in + in + range should lower");
5173
5174 let path_len = path_query.path.len();
5175 assert_eq!(path_query.path[path_len - 3], b"age".to_vec());
5176 assert_eq!(
5177 path_query.path.last().expect("path should not be empty"),
5178 &b"firstName".to_vec()
5179 );
5180
5181 let outer = &path_query.query.query;
5182 assert_eq!(outer.items.len(), 2);
5183 assert_eq!(
5184 outer.default_subquery_branch.subquery_path,
5185 Some(vec![b"middleName".to_vec()])
5186 );
5187 let middle = outer
5188 .default_subquery_branch
5189 .subquery
5190 .as_deref()
5191 .expect("expected a middleName subquery");
5192 assert_eq!(middle.items.len(), 2);
5193 assert_eq!(
5194 middle.default_subquery_branch.subquery_path,
5195 Some(vec![b"lastName".to_vec()])
5196 );
5197 let range_level = middle
5198 .default_subquery_branch
5199 .subquery
5200 .as_deref()
5201 .expect("expected a lastName subquery");
5202 assert_eq!(range_level.items.len(), 1);
5204 assert_eq!(
5205 range_level.default_subquery_branch.subquery_path,
5206 Some(vec![vec![0]])
5207 );
5208 }
5209
5210 #[test]
5211 fn cross_product_above_cap_is_rejected() {
5212 let contract = family_contract();
5213 let first_names: Vec<String> = (0..20).map(|i| format!("First{i:02}")).collect();
5214 let last_names: Vec<String> = (0..6).map(|i| format!("Last{i}")).collect();
5215 let query = person_query(
5216 &contract,
5217 vec![
5218 WhereClause {
5219 field: "firstName".to_string(),
5220 operator: WhereOperator::In,
5221 value: Value::Array(first_names.iter().cloned().map(Value::Text).collect()),
5222 },
5223 WhereClause {
5224 field: "lastName".to_string(),
5225 operator: WhereOperator::In,
5226 value: Value::Array(last_names.iter().cloned().map(Value::Text).collect()),
5227 },
5228 ],
5229 &["firstName", "lastName"],
5230 );
5231
5232 let error = query
5233 .construct_path_query(None, PlatformVersion::latest())
5234 .expect_err("a 120-branch cross product must be rejected");
5235 assert!(
5236 matches!(error, Error::Query(QuerySyntaxError::InvalidInClause(_))),
5237 "expected InvalidInClause, got {error:?}"
5238 );
5239 }
5240
5241 #[test]
5242 fn non_consecutive_in_fields_are_rejected() {
5243 let contract = family_contract();
5244 let query = person_query(
5248 &contract,
5249 vec![
5250 in_clause("middleName", &["Ivanna", "Evangeline"]),
5251 in_clause("lastName", &["Kriskov", "Randolf"]),
5252 ],
5253 &["middleName", "lastName"],
5254 );
5255
5256 let error = query
5257 .construct_path_query(None, PlatformVersion::latest())
5258 .expect_err("non-consecutive in clauses must be rejected");
5259 assert!(
5260 matches!(
5261 error,
5262 Error::Query(QuerySyntaxError::WhereClauseOnNonIndexedProperty(_))
5263 ),
5264 "expected WhereClauseOnNonIndexedProperty, got {error:?}"
5265 );
5266 }
5267
5268 #[test]
5269 fn cursor_pagination_is_rejected() {
5270 let contract = family_contract();
5271 let mut query = person_query(
5272 &contract,
5273 vec![
5274 in_clause("firstName", &["Adey", "Briney"]),
5275 in_clause("lastName", &["Kriskov", "Randolf"]),
5276 ],
5277 &["firstName", "lastName"],
5278 );
5279 query.start_at = Some([5u8; 32]);
5280 query.start_at_included = false;
5281
5282 let error = query
5283 .construct_path_query(None, PlatformVersion::latest())
5284 .expect_err("cursor pagination with multiple in clauses must be rejected");
5285 assert!(
5286 matches!(error, Error::Query(QuerySyntaxError::Unsupported(_))),
5287 "expected Unsupported, got {error:?}"
5288 );
5289 }
5290
5291 #[test]
5292 fn missing_order_by_on_an_in_field_is_rejected() {
5293 let contract = family_contract();
5294 let query = person_query(
5295 &contract,
5296 vec![
5297 in_clause("firstName", &["Adey", "Briney"]),
5298 in_clause("lastName", &["Kriskov", "Randolf"]),
5299 ],
5300 &["firstName"],
5301 );
5302
5303 let error = query
5304 .construct_path_query(None, PlatformVersion::latest())
5305 .expect_err("missing order by on an in field must be rejected");
5306 assert!(
5311 matches!(
5312 error,
5313 Error::Query(QuerySyntaxError::WhereClauseOnNonIndexedProperty(_))
5314 ),
5315 "expected WhereClauseOnNonIndexedProperty, got {error:?}"
5316 );
5317 }
5318 }
5319}