1use dpp::data_contract::document_type::{DocumentPropertyType, TimeRangeTransform};
2use std::sync::Arc;
3
4#[cfg(any(feature = "server", feature = "verify"))]
5pub use {
6 chained_document_query::{ChainedDocumentsResult, MAX_CHAINED_JOIN_VALUES},
10 composite_document_query::{
14 BindingSource, CompositeDocumentsResult, DriveSubQuery, SubQueryBinding, SubQueryKind,
15 SubQueryResult, MAX_BOUND_VALUES, MAX_SUB_QUERIES,
16 },
17 conditions::{ValueClause, WhereClause, WhereOperator},
18 drive_document_average_query::{AverageEntry, AverageMode},
23 drive_document_count_query::{
30 CountMode, DocumentCountMode, DriveDocumentCountQuery, SplitCountEntry,
31 },
32 drive_document_having_query::{
38 AxisRangeBounds, DocumentHavingMode, DriveDocumentHavingQuery, MAX_HAVING_LIMIT,
39 },
40 drive_document_ranked_query::{
46 DocumentRankedMode, DriveDocumentRankedQuery, RankedAxis, RankedEntry, RankedEntryValue,
47 RankedPage, RankedPaginationInputs, MAX_RANKED_LIMIT, RANKED_AVG_SCALE,
48 RANKED_COUNT_ORDER_KEY,
49 },
50 drive_document_sum_query::{DriveDocumentSumQuery, SumEntry, SumMode},
55 grovedb::{PathQuery, Query, QueryItem, SizedQuery},
56 having::{
57 HavingAggregate, HavingAggregateFunction, HavingClause, HavingOperator, HavingRightOperand,
58 },
59 ordering::OrderClause,
60 projection::{SelectFunction, SelectProjection},
61 single_document_drive_query::SingleDocumentDriveQuery,
62 single_document_drive_query::SingleDocumentDriveQueryContestedStatus,
63 vote_polls_by_end_date_query::VotePollsByEndDateDriveQuery,
64 vote_query::IdentityBasedVoteDriveQuery,
65};
66
67#[cfg(feature = "server")]
70pub use drive_document_count_query::{
71 DocumentCountRequest, DocumentCountResponse, RangeCountOptions, MAX_LIMIT_AS_FAILSAFE,
72};
73
74#[cfg(feature = "server")]
78pub use drive_document_sum_query::{
79 DocumentSumRequest, DocumentSumResponse, RangeSumOptions, RangeSumWalkMode,
80};
81
82#[cfg(feature = "server")]
86pub use drive_document_average_query::{DocumentAverageRequest, DocumentAverageResponse};
87
88#[cfg(feature = "server")]
93pub use drive_document_ranked_query::{DocumentRankedRequest, DocumentRankedResponse};
94
95#[cfg(feature = "server")]
100pub use drive_document_having_query::{DocumentHavingRequest, DocumentHavingResponse};
101#[cfg(any(feature = "server", feature = "verify"))]
103use {
104 crate::{
105 drive::contract::paths::DataContractPaths,
106 error::{drive::DriveError, query::QuerySyntaxError, Error},
107 },
108 dpp::{
109 data_contract::{
110 accessors::v0::DataContractV0Getters,
111 document_type::{accessors::DocumentTypeV0Getters, methods::DocumentTypeV0Methods},
112 document_type::{DocumentTypeRef, Index},
113 DataContract,
114 },
115 document::{document_methods::DocumentMethodsV0, Document},
116 platform_value::{btreemap_extensions::BTreeValueRemoveFromMapHelper, Value},
117 version::PlatformVersion,
118 ProtocolError,
119 },
120 indexmap::IndexMap,
121 sqlparser::{
122 ast::{self, OrderByExpr, Select, Statement, TableFactor::Table, Value::Number},
123 dialect::MySqlDialect,
124 parser::Parser,
125 },
126 std::{collections::BTreeMap, ops::BitXor},
127};
128
129#[cfg(all(feature = "server", feature = "verify"))]
130use crate::verify::RootHash;
131
132#[cfg(feature = "server")]
133use dpp::document::serialization_traits::DocumentPlatformConversionMethodsV0;
134#[cfg(feature = "server")]
135pub use grovedb::{
136 query_result_type::{QueryResultElements, QueryResultType},
137 Element, Error as GroveError, TransactionArg,
138};
139
140use dpp::document;
141use dpp::prelude::Identifier;
142use dpp::validation::{SimpleValidationResult, ValidationResult};
143#[cfg(feature = "server")]
144use {
145 crate::{drive::Drive, fees::op::LowLevelDriveOperation},
146 dpp::block::block_info::BlockInfo,
147};
148use crate::config::DriveConfig;
150use crate::util::common::encode::encode_u64;
152#[cfg(feature = "server")]
153use crate::util::grove_operations::QueryType::StatefulQuery;
154
155#[cfg(any(feature = "server", feature = "verify"))]
157pub mod canonicalize;
158#[cfg(any(feature = "server", feature = "verify"))]
159pub use canonicalize::validate_and_canonicalize_where_clauses;
160#[cfg(any(feature = "server", feature = "verify"))]
161pub mod conditions;
162#[cfg(any(feature = "server", feature = "verify"))]
163mod defaults;
164#[cfg(any(feature = "server", feature = "verify"))]
165pub mod having;
166mod non_primary_key_path_query;
167#[cfg(any(feature = "server", feature = "verify"))]
168pub mod ordering;
169#[cfg(any(feature = "server", feature = "verify"))]
170pub mod projection;
171#[cfg(any(feature = "server", feature = "verify"))]
172mod single_document_drive_query;
173pub(crate) mod where_clause_grouping;
175
176#[cfg(feature = "server")]
178mod test_index;
179
180#[cfg(any(feature = "server", feature = "verify"))]
181pub mod vote_poll_vote_state_query;
183#[cfg(any(feature = "server", feature = "verify"))]
184pub mod vote_query;
186
187#[cfg(any(feature = "server", feature = "verify"))]
188pub mod vote_poll_contestant_votes_query;
190
191#[cfg(any(feature = "server", feature = "verify"))]
192pub mod vote_polls_by_end_date_query;
194
195#[cfg(any(feature = "server", feature = "verify"))]
196pub mod vote_polls_by_document_type_query;
198
199#[cfg(any(feature = "server", feature = "verify"))]
205pub type ContractLookupFn<'a> =
206 dyn Fn(&Identifier) -> Result<Option<Arc<DataContract>>, Error> + 'a;
207
208#[cfg(any(feature = "server", feature = "verify"))]
219pub fn contract_lookup_fn_for_contract<'a>(
220 data_contract: Arc<DataContract>,
221) -> Box<ContractLookupFn<'a>> {
222 let func = move |id: &Identifier| -> Result<Option<Arc<DataContract>>, Error> {
223 if data_contract.id().ne(id) {
224 return Ok(None);
225 }
226 Ok(Some(Arc::clone(&data_contract)))
227 };
228 Box::new(func)
229}
230
231#[cfg(any(feature = "server", feature = "verify"))]
233pub mod contested_resource_votes_given_by_identity_query;
234#[cfg(any(feature = "server", feature = "verify"))]
236pub mod drive_contested_document_query;
237
238#[cfg(any(feature = "server", feature = "verify"))]
240pub mod proposer_block_count_query;
241
242#[cfg(any(feature = "server", feature = "verify"))]
244pub mod identity_token_balance_drive_query;
245#[cfg(any(feature = "server", feature = "verify"))]
247pub mod identity_token_info_drive_query;
248
249#[cfg(any(feature = "server", feature = "verify"))]
251pub mod filter;
252#[cfg(any(feature = "server", feature = "verify"))]
254pub mod token_status_drive_query;
255
256#[cfg(any(feature = "server", feature = "verify"))]
258pub mod drive_document_count_query;
259
260#[cfg(any(feature = "server", feature = "verify"))]
266pub mod drive_document_sum_query;
267
268#[cfg(any(feature = "server", feature = "verify"))]
275pub mod drive_document_average_query;
276
277#[cfg(any(feature = "server", feature = "verify"))]
283pub mod drive_document_having_query;
284
285#[cfg(any(feature = "server", feature = "verify"))]
292pub mod drive_document_ranked_query;
293
294#[cfg(any(feature = "server", feature = "verify"))]
299pub(crate) mod index_only_synthesis;
300
301#[cfg(any(feature = "server", feature = "verify"))]
307pub mod chained_document_query;
308
309#[cfg(any(feature = "server", feature = "verify"))]
313pub mod composite_document_query;
314
315#[cfg(feature = "server")]
320pub mod drive_document_count_and_sum_query;
321
322pub type QuerySyntaxValidationResult<TData> = ValidationResult<TData, QuerySyntaxError>;
324
325pub type QuerySyntaxSimpleValidationResult = SimpleValidationResult<QuerySyntaxError>;
327
328#[cfg(any(feature = "server", feature = "verify"))]
329#[derive(Debug, Clone)]
336pub struct StartAtDocument<'a> {
337 pub document: Document,
339
340 pub document_type: DocumentTypeRef<'a>,
342
343 pub included: bool,
347}
348
349#[cfg(any(feature = "server", feature = "verify"))]
351#[derive(Clone, Debug, PartialEq, Default)]
352pub struct InternalClauses {
353 pub primary_key_in_clause: Option<WhereClause>,
355 pub primary_key_equal_clause: Option<WhereClause>,
357 pub in_clauses: Vec<WhereClause>,
364 pub range_clause: Option<WhereClause>,
371 pub equal_clauses: BTreeMap<String, WhereClause>,
373}
374
375#[cfg(any(feature = "server", feature = "verify"))]
381#[derive(Debug, Clone, Copy, Default, PartialEq, Eq)]
382pub struct ClauseFieldRoles {
383 pub primary_key: bool,
385 pub index_property: bool,
387 pub terminal: bool,
390}
391
392#[cfg(any(feature = "server", feature = "verify"))]
393impl ClauseFieldRoles {
394 pub fn unindexed(&self) -> bool {
397 !self.primary_key && !self.index_property && !self.terminal
398 }
399}
400
401#[cfg(any(feature = "server", feature = "verify"))]
408pub(crate) enum BestIndexOutcome<'a> {
409 Matched(&'a Index),
411 NoIndexMatches(Error),
414}
415
416impl InternalClauses {
417 #[cfg(any(feature = "server", feature = "verify"))]
423 pub fn classify_field(document_type: DocumentTypeRef, field: &str) -> ClauseFieldRoles {
424 let mut roles = ClauseFieldRoles {
425 primary_key: field == "$id",
426 ..Default::default()
427 };
428 for index in document_type.indexes().values() {
429 if index
430 .properties
431 .iter()
432 .any(|property| property.name == field)
433 {
434 roles.index_property = true;
435 }
436 if index.terminal.as_deref() == Some(field) {
437 roles.terminal = true;
438 }
439 if roles.index_property && roles.terminal {
440 break;
441 }
442 }
443 roles
444 }
445
446 #[cfg(any(feature = "server", feature = "verify"))]
450 pub fn classify_fields(
451 &self,
452 document_type: DocumentTypeRef,
453 ) -> BTreeMap<String, ClauseFieldRoles> {
454 let mut classified = BTreeMap::new();
455 let mut add = |field: &str| {
456 classified
457 .entry(field.to_string())
458 .or_insert_with(|| Self::classify_field(document_type, field));
459 };
460 if self.primary_key_equal_clause.is_some() || self.primary_key_in_clause.is_some() {
461 add("$id");
462 }
463 for field in self.equal_clauses.keys() {
464 add(field);
465 }
466 if let Some(range_clause) = &self.range_clause {
467 add(&range_clause.field);
468 }
469 for in_clause in &self.in_clauses {
470 add(&in_clause.field);
471 }
472 classified
473 }
474
475 #[cfg(any(feature = "server", feature = "verify"))]
476 pub fn verify(&self) -> bool {
478 if self
480 .primary_key_in_clause
481 .is_some()
482 .bitxor(self.primary_key_equal_clause.is_some())
483 {
484 !(!self.in_clauses.is_empty()
486 || self.range_clause.is_some()
487 || !self.equal_clauses.is_empty())
488 } else {
489 !(self.primary_key_in_clause.is_some() && self.primary_key_equal_clause.is_some())
490 }
491 }
492
493 #[cfg(any(feature = "server", feature = "verify"))]
494 pub fn is_for_primary_key(&self) -> bool {
496 self.primary_key_in_clause.is_some() || self.primary_key_equal_clause.is_some()
497 }
498
499 #[cfg(any(feature = "server", feature = "verify"))]
500 pub fn is_empty(&self) -> bool {
502 self.in_clauses.is_empty()
503 && self.range_clause.is_none()
504 && self.equal_clauses.is_empty()
505 && self.primary_key_in_clause.is_none()
506 && self.primary_key_equal_clause.is_none()
507 }
508
509 #[cfg(any(feature = "server", feature = "verify"))]
510 pub fn extract_from_clauses(
512 all_where_clauses: Vec<WhereClause>,
513 platform_version: &PlatformVersion,
514 ) -> Result<Self, Error> {
515 let primary_key_equal_clauses_array = all_where_clauses
516 .iter()
517 .filter_map(|where_clause| match where_clause.operator {
518 WhereOperator::Equal => match where_clause.is_identifier() {
519 true => Some(where_clause.clone()),
520 false => None,
521 },
522 _ => None,
523 })
524 .collect::<Vec<WhereClause>>();
525
526 let primary_key_in_clauses_array = all_where_clauses
527 .iter()
528 .filter_map(|where_clause| match where_clause.operator {
529 WhereOperator::In => match where_clause.is_identifier() {
530 true => Some(where_clause.clone()),
531 false => None,
532 },
533 _ => None,
534 })
535 .collect::<Vec<WhereClause>>();
536
537 let (equal_clauses, range_clause, in_clauses) =
538 WhereClause::group_clauses(&all_where_clauses, platform_version)?;
539
540 let primary_key_equal_clause = match primary_key_equal_clauses_array.len() {
541 0 => Ok(None),
542 1 => Ok(Some(
543 primary_key_equal_clauses_array
544 .first()
545 .expect("there must be a value")
546 .clone(),
547 )),
548 _ => Err(Error::Query(
549 QuerySyntaxError::DuplicateNonGroupableClauseSameField(
550 "There should only be one equal clause for the primary key",
551 ),
552 )),
553 }?;
554
555 let primary_key_in_clause = match primary_key_in_clauses_array.len() {
556 0 => Ok(None),
557 1 => Ok(Some(
558 primary_key_in_clauses_array
559 .first()
560 .expect("there must be a value")
561 .clone(),
562 )),
563 _ => Err(Error::Query(
564 QuerySyntaxError::DuplicateNonGroupableClauseSameField(
565 "There should only be one in clause for the primary key",
566 ),
567 )),
568 }?;
569
570 let internal_clauses = InternalClauses {
571 primary_key_equal_clause,
572 primary_key_in_clause,
573 in_clauses,
574 range_clause,
575 equal_clauses,
576 };
577
578 match internal_clauses.verify() {
579 true => Ok(internal_clauses),
580 false => Err(Error::Query(
581 QuerySyntaxError::InvalidWhereClauseComponents("Query has invalid where clauses"),
582 )),
583 }
584 }
585
586 #[cfg(any(feature = "server", feature = "verify"))]
588 pub fn validate_against_schema(
589 &self,
590 document_type: DocumentTypeRef,
591 ) -> QuerySyntaxSimpleValidationResult {
592 if !self.verify() {
594 return QuerySyntaxSimpleValidationResult::new_with_error(
595 QuerySyntaxError::InvalidWhereClauseComponents(
596 "invalid composition of where clauses",
597 ),
598 );
599 }
600
601 for in_clause in &self.in_clauses {
603 if in_clause.field == "$id" {
605 return QuerySyntaxSimpleValidationResult::new_with_error(
606 QuerySyntaxError::InvalidWhereClauseComponents(
607 "use primary_key_* clauses for $id",
608 ),
609 );
610 }
611 let result = in_clause.validate_against_schema(document_type);
612 if !result.is_valid() {
613 return result;
614 }
615 }
616
617 if let Some(range_clause) = &self.range_clause {
619 if range_clause.field == "$id" {
621 return QuerySyntaxSimpleValidationResult::new_with_error(
622 QuerySyntaxError::InvalidWhereClauseComponents(
623 "use primary_key_* clauses for $id",
624 ),
625 );
626 }
627 let result = range_clause.validate_against_schema(document_type);
628 if !result.is_valid() {
629 return result;
630 }
631 }
632
633 for (field, eq_clause) in &self.equal_clauses {
635 if field.as_str() == "$id" {
637 return QuerySyntaxSimpleValidationResult::new_with_error(
638 QuerySyntaxError::InvalidWhereClauseComponents(
639 "use primary_key_* clauses for $id",
640 ),
641 );
642 }
643 let result = eq_clause.validate_against_schema(document_type);
644 if !result.is_valid() {
645 return result;
646 }
647 }
648
649 if let Some(pk_eq) = &self.primary_key_equal_clause {
651 if pk_eq.operator != WhereOperator::Equal
652 || !matches!(pk_eq.value, Value::Identifier(_))
653 {
654 return QuerySyntaxSimpleValidationResult::new_with_error(
655 QuerySyntaxError::InvalidWhereClauseComponents(
656 "primary key equality must compare an identifier",
657 ),
658 );
659 }
660 }
661 if let Some(pk_in) = &self.primary_key_in_clause {
662 if pk_in.operator != WhereOperator::In {
663 return QuerySyntaxSimpleValidationResult::new_with_error(
664 QuerySyntaxError::InvalidWhereClauseComponents(
665 "primary key IN must use IN operator",
666 ),
667 );
668 }
669 let result = pk_in.in_values();
671 if !result.is_valid() {
672 return QuerySyntaxSimpleValidationResult::new_with_errors(result.errors);
673 }
674 if let Value::Array(arr) = &pk_in.value {
675 if !arr.iter().all(|v| matches!(v, Value::Identifier(_))) {
676 return QuerySyntaxSimpleValidationResult::new_with_error(
677 QuerySyntaxError::InvalidWhereClauseComponents(
678 "primary key IN must contain identifiers",
679 ),
680 );
681 }
682 } else {
683 return QuerySyntaxSimpleValidationResult::new_with_error(
684 QuerySyntaxError::InvalidWhereClauseComponents(
685 "primary key IN must contain an array of identifiers",
686 ),
687 );
688 }
689 }
690
691 QuerySyntaxSimpleValidationResult::default()
692 }
693}
694
695impl From<InternalClauses> for Vec<WhereClause> {
696 fn from(clauses: InternalClauses) -> Self {
697 let mut result: Self = clauses.equal_clauses.into_values().collect();
698
699 result.extend(clauses.in_clauses);
700 if let Some(clause) = clauses.primary_key_equal_clause {
701 result.push(clause);
702 };
703 if let Some(clause) = clauses.primary_key_in_clause {
704 result.push(clause);
705 };
706 if let Some(clause) = clauses.range_clause {
707 result.push(clause);
708 };
709
710 result
711 }
712}
713
714#[cfg(any(feature = "server", feature = "verify"))]
724#[derive(Copy, Clone, Debug, PartialEq, Eq)]
725#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
726#[cfg_attr(feature = "serde", serde(rename_all = "lowercase"))]
727pub enum TimeRangeSelector {
728 Newest,
731 Oldest,
734 ByStart {
742 start_ms: u64,
744 },
745}
746
747#[cfg(any(feature = "server", feature = "verify"))]
748impl TimeRangeSelector {
749 pub fn as_str(&self) -> &'static str {
759 match self {
760 TimeRangeSelector::Newest => "newest",
761 TimeRangeSelector::Oldest => "oldest",
762 TimeRangeSelector::ByStart { .. } => "byStart",
763 }
764 }
765
766 pub fn from_string(value: &str) -> Option<Self> {
770 match value {
771 "newest" => Some(TimeRangeSelector::Newest),
772 "oldest" => Some(TimeRangeSelector::Oldest),
773 _ => None,
774 }
775 }
776}
777
778#[cfg(any(feature = "server", feature = "verify"))]
787#[derive(Debug, Clone, Copy, PartialEq, Eq)]
788#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
789pub struct TimeRangeGridSpec {
790 pub range_seconds: u64,
792 pub step_seconds: u64,
794 pub phase_seconds: u64,
796}
797
798#[cfg(any(feature = "server", feature = "verify"))]
799impl TimeRangeGridSpec {
800 pub fn matches(&self, transform: &TimeRangeTransform) -> bool {
802 self.range_seconds == transform.range_seconds
803 && self.step_seconds == transform.step_seconds
804 && self.phase_seconds == transform.phase_seconds
805 }
806}
807
808#[cfg(any(feature = "server", feature = "verify"))]
817#[derive(Debug, Clone, PartialEq)]
818pub struct ResolvedTimeRange {
819 pub transform: TimeRangeTransform,
823}
824
825#[cfg(any(feature = "server", feature = "verify"))]
826impl ResolvedTimeRange {
827 pub fn field(&self) -> &str {
830 &self.transform.source
831 }
832}
833
834#[cfg(any(feature = "server", feature = "verify"))]
862pub fn resolve_time_range_bucket_clause(
863 field: &str,
864 selector: TimeRangeSelector,
865 grid: Option<TimeRangeGridSpec>,
866 document_type: DocumentTypeRef,
867 block_time_ms: u64,
868) -> Result<(WhereClause, ResolvedTimeRange), Error> {
869 let mut grids: Vec<&TimeRangeTransform> = Vec::new();
872 for index in document_type.indexes().values() {
873 if let Some(transform) = index
874 .time_range
875 .as_ref()
876 .filter(|transform| transform.source == field)
877 {
878 if !grids.contains(&transform) {
879 grids.push(transform);
880 }
881 }
882 }
883 if grids.is_empty() {
884 return Err(Error::Query(
885 QuerySyntaxError::WhereClauseOnNonIndexedProperty(format!(
886 "no time-range index is defined on field \"{}\"",
887 field
888 )),
889 ));
890 }
891
892 let transform = match grid {
893 Some(spec) => *grids
894 .iter()
895 .find(|transform| spec.matches(transform))
896 .ok_or(Error::Query(QuerySyntaxError::Unsupported(format!(
897 "no time-range index on \"{}\" declares the grid range={}s step={}s phase={}s",
898 field, spec.range_seconds, spec.step_seconds, spec.phase_seconds
899 ))))?,
900 None => {
901 if grids.len() > 1 {
902 return Err(Error::Query(QuerySyntaxError::Unsupported(format!(
903 "field \"{}\" is bucketed by {} different grids; the IN_TIME_RANGE \
904 selection must name one in its `grid` (range/step/phase, in seconds, \
905 as the contract declares them)",
906 field,
907 grids.len()
908 ))));
909 }
910 grids[0]
911 }
912 };
913
914 let bucket_start = match selector {
915 TimeRangeSelector::Newest => transform.newest_active_start(block_time_ms),
916 TimeRangeSelector::Oldest => transform.oldest_active_start(block_time_ms),
917 TimeRangeSelector::ByStart { start_ms } => {
923 if !transform.is_bucket_start(start_ms) {
924 return Err(Error::Query(QuerySyntaxError::Unsupported(format!(
925 "byStart {} on \"{}\" is not a window start of the grid range={}s \
926 step={}s phase={}s: starts are phase + k*step on the millisecond \
927 timeline, and an off-grid start is rejected rather than snapped",
928 start_ms,
929 field,
930 transform.range_seconds,
931 transform.step_seconds,
932 transform.phase_seconds
933 ))));
934 }
935 if transform.bucket_expired(start_ms, block_time_ms) {
945 return Err(Error::Query(QuerySyntaxError::Unsupported(format!(
946 "byStart {} on \"{}\" is past the ttl horizon ({}s): expired windows \
947 drain lazily and may be mid-removal, so they are not queryable — \
948 entries under this index live at most `ttl` past their window's start",
949 start_ms,
950 field,
951 transform.ttl_seconds.unwrap_or_default()
952 ))));
953 }
954 Some(start_ms)
955 }
956 }
957 .ok_or(Error::Query(QuerySyntaxError::Unsupported(format!(
958 "no time range on \"{}\" is active yet: the block time predates the grid's phase \
959 anchor (only possible within the first step after the epoch)",
960 field
961 ))))?;
962
963 Ok((
964 WhereClause {
965 field: field.to_string(),
966 operator: WhereOperator::Equal,
967 value: Value::U64(bucket_start),
968 },
969 ResolvedTimeRange {
970 transform: transform.clone(),
971 },
972 ))
973}
974
975#[cfg(any(feature = "server", feature = "verify"))]
1002pub fn index_admissible_for_resolved_time_range(
1003 index: &Index,
1004 resolved_time_ranges: &[ResolvedTimeRange],
1005) -> bool {
1006 match resolved_time_ranges {
1007 [] => index.time_range.is_none(),
1008 [resolved] => index
1015 .time_range
1016 .as_ref()
1017 .is_some_and(|transform| *transform == resolved.transform),
1018 _ => false,
1019 }
1020}
1021
1022#[cfg(any(feature = "server", feature = "verify"))]
1040pub fn index_admissible_for_skip_if_absent(index: &Index, fields: &[&str]) -> bool {
1041 if !index.skip_if_absent {
1042 return true;
1043 }
1044 index
1045 .properties
1046 .first()
1047 .is_some_and(|trigger| fields.contains(&trigger.name.as_str()))
1048}
1049
1050#[cfg(any(feature = "server", feature = "verify"))]
1065pub fn validate_resolved_time_range_clause_shapes(
1066 where_clauses: &[WhereClause],
1067 resolved_time_ranges: &[ResolvedTimeRange],
1068) -> Result<(), Error> {
1069 for field in resolved_time_ranges.iter().map(|resolved| resolved.field()) {
1070 let mut equalities = 0usize;
1071 for clause in where_clauses.iter().filter(|c| c.field == field) {
1072 if clause.operator == WhereOperator::Equal {
1073 equalities += 1;
1074 } else {
1075 return Err(Error::Query(
1076 QuerySyntaxError::InvalidWhereClauseComponents(
1077 "a time-range-resolved field may only carry the single equality its \
1078 resolution produced, not a range or In clause",
1079 ),
1080 ));
1081 }
1082 }
1083 if equalities != 1 {
1084 return Err(Error::Query(
1085 QuerySyntaxError::InvalidWhereClauseComponents(
1086 "a time-range-resolved field must carry exactly one equality clause — the \
1087 one its resolution produced",
1088 ),
1089 ));
1090 }
1091 }
1092 Ok(())
1093}
1094
1095#[cfg(any(feature = "server", feature = "verify"))]
1096#[derive(Debug, PartialEq, Clone)]
1098pub struct DriveDocumentQuery<'a> {
1099 pub contract: &'a DataContract,
1101 pub document_type: DocumentTypeRef<'a>,
1103 pub internal_clauses: InternalClauses,
1105 pub offset: Option<u16>,
1107 pub limit: Option<u16>,
1109 pub order_by: IndexMap<String, OrderClause>,
1111 pub start_at: Option<[u8; 32]>,
1113 pub start_at_included: bool,
1115 pub block_time_ms: Option<u64>,
1117 pub resolved_time_ranges: Vec<ResolvedTimeRange>,
1133 pub sub_queries: Vec<DriveSubQuery<'a>>,
1152}
1153
1154impl<'a> DriveDocumentQuery<'a> {
1155 #[cfg(any(feature = "server", feature = "verify"))]
1157 pub fn new_primary_key_single_item_query(
1158 contract: &'a DataContract,
1159 document_type: DocumentTypeRef<'a>,
1160 id: Identifier,
1161 ) -> Self {
1162 DriveDocumentQuery {
1163 contract,
1164 document_type,
1165 internal_clauses: InternalClauses {
1166 primary_key_in_clause: None,
1167 primary_key_equal_clause: Some(WhereClause {
1168 field: document::property_names::ID.to_string(),
1169 operator: WhereOperator::Equal,
1170 value: Value::Identifier(id.to_buffer()),
1171 }),
1172 in_clauses: Vec::new(),
1173 range_clause: None,
1174 equal_clauses: Default::default(),
1175 },
1176 offset: None,
1177 limit: None,
1178 order_by: Default::default(),
1179 start_at: None,
1180 start_at_included: false,
1181 block_time_ms: None,
1182 resolved_time_ranges: vec![],
1183 sub_queries: vec![],
1184 }
1185 }
1186
1187 #[cfg(feature = "server")]
1188 pub fn any_item_query(contract: &'a DataContract, document_type: DocumentTypeRef<'a>) -> Self {
1190 DriveDocumentQuery {
1191 contract,
1192 document_type,
1193 internal_clauses: Default::default(),
1194 offset: None,
1195 limit: Some(1),
1196 order_by: Default::default(),
1197 start_at: None,
1198 start_at_included: true,
1199 block_time_ms: None,
1200 resolved_time_ranges: vec![],
1201 sub_queries: vec![],
1202 }
1203 }
1204
1205 #[cfg(feature = "server")]
1206 pub fn all_items_query(
1208 contract: &'a DataContract,
1209 document_type: DocumentTypeRef<'a>,
1210 limit: Option<u16>,
1211 ) -> Self {
1212 DriveDocumentQuery {
1213 contract,
1214 document_type,
1215 internal_clauses: Default::default(),
1216 offset: None,
1217 limit,
1218 order_by: Default::default(),
1219 start_at: None,
1220 start_at_included: true,
1221 block_time_ms: None,
1222 resolved_time_ranges: vec![],
1223 sub_queries: vec![],
1224 }
1225 }
1226
1227 #[cfg(any(feature = "server", feature = "verify"))]
1228 pub fn with_sub_queries(mut self, sub_queries: Vec<DriveSubQuery<'a>>) -> Self {
1233 self.sub_queries = sub_queries;
1234 self
1235 }
1236
1237 #[cfg(any(feature = "server", feature = "verify"))]
1238 pub fn with_by_id_join(
1251 mut self,
1252 source_property: impl Into<String>,
1253 document_type: DocumentTypeRef<'a>,
1254 ) -> Self {
1255 self.sub_queries.push(DriveSubQuery {
1256 contract: self.contract,
1257 document_type,
1258 kind: SubQueryKind::Documents,
1259 where_clauses: vec![],
1260 order_by: vec![],
1261 limit: None,
1262 binding: Some(SubQueryBinding {
1263 source: BindingSource::Page,
1264 source_property: source_property.into(),
1265 field: document::property_names::ID.to_string(),
1266 }),
1267 });
1268 self
1269 }
1270
1271 #[cfg(any(feature = "server", feature = "verify"))]
1272 pub(crate) fn ensure_no_sub_queries(&self, surface: &str) -> Result<(), Error> {
1277 if self.sub_queries.is_empty() {
1278 return Ok(());
1279 }
1280 Err(Error::Query(QuerySyntaxError::Unsupported(format!(
1281 "this query carries {} sub-queries, which {} would silently ignore; execute and \
1282 verify it on the composite surface (query_composite_documents / \
1283 verify_composite_documents_proof) or, for a single by-id join, the chained one \
1284 (query_chained_documents / verify_chained_documents_proof)",
1285 self.sub_queries.len(),
1286 surface,
1287 ))))
1288 }
1289
1290 #[cfg(any(feature = "server", feature = "verify"))]
1291 pub fn is_for_primary_key(&self) -> bool {
1293 self.internal_clauses.is_for_primary_key()
1294 || (self.internal_clauses.is_empty()
1295 && (self.order_by.is_empty()
1296 || (self.order_by.len() == 1
1297 && self
1298 .order_by
1299 .keys()
1300 .collect::<Vec<&String>>()
1301 .first()
1302 .unwrap()
1303 .as_str()
1304 == "$id")))
1305 }
1306
1307 #[cfg(feature = "cbor_query")]
1308 pub fn from_cbor(
1310 query_cbor: &[u8],
1311 contract: &'a DataContract,
1312 document_type: DocumentTypeRef<'a>,
1313 config: &DriveConfig,
1314 platform_version: &PlatformVersion,
1315 ) -> Result<Self, Error> {
1316 let query_document_value: Value = ciborium::de::from_reader(query_cbor).map_err(|_| {
1317 Error::Query(QuerySyntaxError::DeserializationError(
1318 "unable to decode query from cbor".to_string(),
1319 ))
1320 })?;
1321 Self::from_value(
1322 query_document_value,
1323 contract,
1324 document_type,
1325 config,
1326 platform_version,
1327 )
1328 }
1329
1330 #[cfg(any(feature = "server", feature = "verify"))]
1331 pub fn from_value(
1333 query_value: Value,
1334 contract: &'a DataContract,
1335 document_type: DocumentTypeRef<'a>,
1336 config: &DriveConfig,
1337 platform_version: &PlatformVersion,
1338 ) -> Result<Self, Error> {
1339 let query_document: BTreeMap<String, Value> = query_value.into_btree_string_map()?;
1340 Self::from_btree_map_value(
1341 query_document,
1342 contract,
1343 document_type,
1344 config,
1345 platform_version,
1346 )
1347 }
1348
1349 #[cfg(any(feature = "server", feature = "verify"))]
1350 pub fn from_btree_map_value(
1352 mut query_document: BTreeMap<String, Value>,
1353 contract: &'a DataContract,
1354 document_type: DocumentTypeRef<'a>,
1355 config: &DriveConfig,
1356 platform_version: &PlatformVersion,
1357 ) -> Result<Self, Error> {
1358 if let Some(contract_id) = query_document
1359 .remove_optional_identifier("contract_id")
1360 .map_err(|e| Error::Protocol(Box::new(ProtocolError::ValueError(e))))?
1361 {
1362 if contract.id() != contract_id {
1363 return Err(ProtocolError::IdentifierError(format!(
1364 "data contract id mismatch, expected: {}, got: {}",
1365 contract.id(),
1366 contract_id
1367 ))
1368 .into());
1369 };
1370 }
1371
1372 if let Some(document_type_name) = query_document
1373 .remove_optional_string("document_type_name")
1374 .map_err(|e| Error::Protocol(Box::new(ProtocolError::ValueError(e))))?
1375 {
1376 if document_type.name() != &document_type_name {
1377 return Err(ProtocolError::IdentifierError(format!(
1378 "document type name mismatch, expected: {}, got: {}",
1379 document_type.name(),
1380 document_type_name
1381 ))
1382 .into());
1383 }
1384 }
1385
1386 let maybe_limit: Option<u16> = query_document
1387 .remove_optional_integer("limit")
1388 .map_err(|e| Error::Protocol(Box::new(ProtocolError::ValueError(e))))?;
1389
1390 let limit = maybe_limit
1391 .map_or(Some(config.default_query_limit), |limit_value| {
1392 if limit_value == 0 || limit_value > config.default_query_limit {
1393 None
1394 } else {
1395 Some(limit_value)
1396 }
1397 })
1398 .ok_or(Error::Query(QuerySyntaxError::InvalidLimit(format!(
1399 "limit greater than max limit {}",
1400 config.max_query_limit
1401 ))))?;
1402
1403 let offset: Option<u16> = query_document
1404 .remove_optional_integer("offset")
1405 .map_err(|e| Error::Protocol(Box::new(ProtocolError::ValueError(e))))?;
1406
1407 let block_time_ms: Option<u64> = query_document
1408 .remove_optional_integer("blockTime")
1409 .map_err(|e| Error::Protocol(Box::new(ProtocolError::ValueError(e))))?;
1410
1411 let all_where_clauses: Vec<WhereClause> =
1412 query_document
1413 .remove("where")
1414 .map_or(Ok(vec![]), |id_cbor| {
1415 if let Value::Array(clauses) = id_cbor {
1416 clauses
1417 .iter()
1418 .map(|where_clause| {
1419 if let Value::Array(clauses_components) = where_clause {
1420 WhereClause::from_components(clauses_components)
1421 } else {
1422 Err(Error::Query(QuerySyntaxError::InvalidFormatWhereClause(
1423 "where clause must be an array".to_string(),
1424 )))
1425 }
1426 })
1427 .collect::<Result<Vec<WhereClause>, Error>>()
1428 } else {
1429 Err(Error::Query(QuerySyntaxError::InvalidFormatWhereClause(
1430 "where clause must be an array".to_string(),
1431 )))
1432 }
1433 })?;
1434
1435 let internal_clauses =
1436 InternalClauses::extract_from_clauses(all_where_clauses, platform_version)?;
1437
1438 let start_at_option = query_document.remove("startAt");
1439 let start_after_option = query_document.remove("startAfter");
1440 if start_after_option.is_some() && start_at_option.is_some() {
1441 return Err(Error::Query(QuerySyntaxError::DuplicateStartConditions(
1442 "only one of startAt or startAfter should be provided",
1443 )));
1444 }
1445
1446 let mut start_at_included = true;
1447
1448 let mut start_option: Option<Value> = None;
1449
1450 if start_after_option.is_some() {
1451 start_option = start_after_option;
1452 start_at_included = false;
1453 } else if start_at_option.is_some() {
1454 start_option = start_at_option;
1455 start_at_included = true;
1456 }
1457
1458 let start_at: Option<[u8; 32]> = start_option
1459 .map(|v| {
1460 v.into_identifier()
1461 .map_err(|e| Error::Protocol(Box::new(ProtocolError::ValueError(e))))
1462 .map(|identifier| identifier.into_buffer())
1463 })
1464 .transpose()?;
1465
1466 let order_by: IndexMap<String, OrderClause> =
1467 query_document
1468 .remove("orderBy")
1469 .map_or(Ok(IndexMap::new()), |id_cbor| {
1470 if let Value::Array(clauses) = id_cbor {
1471 clauses
1472 .into_iter()
1473 .filter_map(|order_clause| {
1474 if let Value::Array(clauses_components) = order_clause {
1475 let order_clause =
1476 OrderClause::from_components(&clauses_components)
1477 .map_err(Error::from);
1478 match order_clause {
1479 Ok(order_clause) => {
1480 Some(Ok((order_clause.field.clone(), order_clause)))
1481 }
1482 Err(err) => Some(Err(err)),
1483 }
1484 } else {
1485 None
1486 }
1487 })
1488 .collect::<Result<IndexMap<String, OrderClause>, Error>>()
1489 } else {
1490 Err(Error::Query(QuerySyntaxError::InvalidOrderByProperties(
1491 "order clauses must be an array",
1492 )))
1493 }
1494 })?;
1495
1496 if !query_document.is_empty() {
1497 return Err(Error::Query(QuerySyntaxError::Unsupported(format!(
1498 "unsupported syntax in where clause: {:?}",
1499 query_document
1500 ))));
1501 }
1502
1503 Ok(DriveDocumentQuery {
1504 contract,
1505 document_type,
1506 internal_clauses,
1507 limit: Some(limit),
1508 offset,
1509 order_by,
1510 start_at,
1511 start_at_included,
1512 block_time_ms,
1513 resolved_time_ranges: vec![],
1514 sub_queries: vec![],
1515 })
1516 }
1517
1518 #[cfg(any(feature = "server", feature = "verify"))]
1519 #[allow(clippy::too_many_arguments)]
1521 pub fn from_decomposed_values(
1522 where_clause: Value,
1523 order_by: Option<Value>,
1524 maybe_limit: Option<u16>,
1525 start_at: Option<[u8; 32]>,
1526 start_at_included: bool,
1527 block_time_ms: Option<u64>,
1528 contract: &'a DataContract,
1529 document_type: DocumentTypeRef<'a>,
1530 config: &DriveConfig,
1531 platform_version: &PlatformVersion,
1532 ) -> Result<Self, Error> {
1533 let all_where_clauses: Vec<WhereClause> = match where_clause {
1534 Value::Null => Ok(vec![]),
1535 Value::Array(clauses) => clauses
1536 .iter()
1537 .map(|where_clause| {
1538 if let Value::Array(clauses_components) = where_clause {
1539 WhereClause::from_components(clauses_components)
1540 } else {
1541 Err(Error::Query(QuerySyntaxError::InvalidFormatWhereClause(
1542 "where clause must be an array".to_string(),
1543 )))
1544 }
1545 })
1546 .collect::<Result<Vec<WhereClause>, Error>>(),
1547 _ => Err(Error::Query(QuerySyntaxError::InvalidFormatWhereClause(
1548 "where clause must be an array".to_string(),
1549 ))),
1550 }?;
1551
1552 let order_by_clauses: Vec<OrderClause> = match order_by {
1560 None | Some(Value::Null) => Vec::new(),
1561 Some(Value::Array(clauses)) => clauses
1562 .iter()
1563 .map(|order_clause| match order_clause {
1564 Value::Array(components) => {
1565 OrderClause::from_components(components).map_err(|_| {
1566 Error::Query(QuerySyntaxError::InvalidOrderByProperties(
1567 "invalid order_by clause components",
1568 ))
1569 })
1570 }
1571 _ => Err(Error::Query(QuerySyntaxError::InvalidOrderByProperties(
1572 "order_by clause must be an array",
1573 ))),
1574 })
1575 .collect::<Result<Vec<_>, _>>()?,
1576 Some(_) => {
1577 return Err(Error::Query(QuerySyntaxError::InvalidOrderByProperties(
1578 "order_by must be an array",
1579 )));
1580 }
1581 };
1582
1583 Self::from_typed_clauses(
1584 all_where_clauses,
1585 order_by_clauses,
1586 maybe_limit,
1587 start_at,
1588 start_at_included,
1589 block_time_ms,
1590 contract,
1591 document_type,
1592 config,
1593 platform_version,
1594 )
1595 }
1596
1597 #[cfg(any(feature = "server", feature = "verify"))]
1617 #[allow(clippy::too_many_arguments)]
1618 pub fn from_typed_clauses(
1619 where_clauses: Vec<WhereClause>,
1620 order_by_clauses: Vec<OrderClause>,
1621 maybe_limit: Option<u16>,
1622 start_at: Option<[u8; 32]>,
1623 start_at_included: bool,
1624 block_time_ms: Option<u64>,
1625 contract: &'a DataContract,
1626 document_type: DocumentTypeRef<'a>,
1627 config: &DriveConfig,
1628 platform_version: &PlatformVersion,
1629 ) -> Result<Self, Error> {
1630 let limit = maybe_limit
1631 .map_or(Some(config.default_query_limit), |limit_value| {
1632 if limit_value == 0 || limit_value > config.default_query_limit {
1633 None
1634 } else {
1635 Some(limit_value)
1636 }
1637 })
1638 .ok_or(Error::Query(QuerySyntaxError::InvalidLimit(format!(
1639 "limit greater than max limit {}",
1640 config.max_query_limit
1641 ))))?;
1642
1643 let internal_clauses =
1644 InternalClauses::extract_from_clauses(where_clauses, platform_version)?;
1645
1646 let order_by: IndexMap<String, OrderClause> = order_by_clauses
1647 .into_iter()
1648 .map(|c| (c.field.clone(), c))
1649 .collect();
1650
1651 Ok(DriveDocumentQuery {
1652 contract,
1653 document_type,
1654 internal_clauses,
1655 offset: None,
1656 limit: Some(limit),
1657 order_by,
1658 start_at,
1659 start_at_included,
1660 block_time_ms,
1661 resolved_time_ranges: vec![],
1662 sub_queries: vec![],
1663 })
1664 }
1665
1666 #[cfg(any(feature = "server", feature = "verify"))]
1667 pub fn from_sql_expr(
1669 sql_string: &str,
1670 contract: &'a DataContract,
1671 config: Option<&DriveConfig>,
1672 platform_version: &PlatformVersion,
1673 ) -> Result<Self, Error> {
1674 let dialect: MySqlDialect = MySqlDialect {};
1675 let statements: Vec<Statement> = Parser::parse_sql(&dialect, sql_string)
1676 .map_err(|e| Error::Query(QuerySyntaxError::SQLParsingError(e)))?;
1677
1678 let first_statement =
1680 statements
1681 .first()
1682 .ok_or(Error::Query(QuerySyntaxError::InvalidSQL(
1683 "Issue parsing sql getting first statement".to_string(),
1684 )))?;
1685
1686 let query: &ast::Query = match first_statement {
1687 ast::Statement::Query(query_struct) => Some(query_struct),
1688 _ => None,
1689 }
1690 .ok_or(Error::Query(QuerySyntaxError::InvalidSQL(
1691 "Issue parsing sql: not a query".to_string(),
1692 )))?;
1693
1694 let max_limit = config
1695 .map(|config| config.max_query_limit)
1696 .unwrap_or(DriveConfig::default().max_query_limit);
1697
1698 let limit: u16 = if let Some(limit_expr) = &query.limit {
1699 match limit_expr {
1700 ast::Expr::Value(Number(num_string, _)) => {
1701 let cast_num_string: &String = num_string;
1702 let user_limit = cast_num_string.parse::<u16>().map_err(|e| {
1703 Error::Query(QuerySyntaxError::InvalidLimit(format!(
1704 "limit could not be parsed {}",
1705 e
1706 )))
1707 })?;
1708 if user_limit > max_limit {
1709 return Err(Error::Query(QuerySyntaxError::InvalidLimit(format!(
1710 "limit {} greater than max limit {}",
1711 user_limit, max_limit
1712 ))));
1713 }
1714 user_limit
1715 }
1716 result => {
1717 return Err(Error::Query(QuerySyntaxError::InvalidLimit(format!(
1718 "expression not a limit {}",
1719 result
1720 ))));
1721 }
1722 }
1723 } else {
1724 config
1725 .map(|config| config.default_query_limit)
1726 .unwrap_or(DriveConfig::default().default_query_limit)
1727 };
1728
1729 let order_by: IndexMap<String, OrderClause> = query
1730 .order_by
1731 .iter()
1732 .map(|order_exp: &OrderByExpr| {
1733 let ascending = order_exp.asc.is_none() || order_exp.asc.unwrap();
1734 let field = order_exp.expr.to_string();
1735 (field.clone(), OrderClause { field, ascending })
1736 })
1737 .collect::<IndexMap<String, OrderClause>>();
1738
1739 let select: &Select = match &*query.body {
1741 ast::SetExpr::Select(select) => Some(select),
1742 _ => None,
1743 }
1744 .ok_or(Error::Query(QuerySyntaxError::InvalidSQL(
1745 "Issue parsing sql: Not a select".to_string(),
1746 )))?;
1747
1748 let document_type_name = match &select
1750 .from
1751 .first()
1752 .ok_or(Error::Query(QuerySyntaxError::InvalidSQL(
1753 "Invalid query: missing from section".to_string(),
1754 )))?
1755 .relation
1756 {
1757 Table { name, .. } => name.0.first().as_ref().map(|identifier| &identifier.value),
1758 _ => None,
1759 }
1760 .ok_or(Error::Query(QuerySyntaxError::InvalidSQL(
1761 "Issue parsing sql: invalid from value".to_string(),
1762 )))?;
1763
1764 let document_type =
1765 contract
1766 .document_types()
1767 .get(document_type_name)
1768 .ok_or(Error::Query(QuerySyntaxError::DocumentTypeNotFound(
1769 "document type not found in contract",
1770 )))?;
1771
1772 let mut all_where_clauses: Vec<WhereClause> = Vec::new();
1782 let selection_tree = select.selection.as_ref();
1783
1784 if let Some(selection_tree) = selection_tree {
1786 WhereClause::build_where_clauses_from_operations(
1787 selection_tree,
1788 document_type,
1789 &mut all_where_clauses,
1790 )?;
1791 }
1792
1793 let internal_clauses =
1794 InternalClauses::extract_from_clauses(all_where_clauses, platform_version)?;
1795
1796 let start_at_option = None; let start_after_option = None; let mut start_at_included = true;
1799 let mut start_option: Option<Value> = None;
1800
1801 if start_after_option.is_some() {
1802 start_option = start_after_option;
1803 start_at_included = false;
1804 } else if start_at_option.is_some() {
1805 start_option = start_at_option;
1806 start_at_included = true;
1807 }
1808
1809 let start_at: Option<[u8; 32]> = start_option
1810 .map(|v| {
1811 v.into_identifier()
1812 .map_err(|e| Error::Protocol(Box::new(ProtocolError::ValueError(e))))
1813 .map(|identifier| identifier.into_buffer())
1814 })
1815 .transpose()?;
1816
1817 Ok(DriveDocumentQuery {
1818 contract,
1819 document_type: document_type.as_ref(),
1820 internal_clauses,
1821 offset: None,
1822 limit: Some(limit),
1823 order_by,
1824 start_at,
1825 start_at_included,
1826 block_time_ms: None,
1827 resolved_time_ranges: vec![],
1828 sub_queries: vec![],
1829 })
1830 }
1831
1832 #[cfg(feature = "cbor_query")]
1837 pub fn to_cbor(&self) -> Result<Vec<u8>, Error> {
1838 let data: BTreeMap<String, Value> = self.into();
1839 let cbor: BTreeMap<String, ciborium::Value> = Value::convert_to_cbor_map(data)?;
1840 let mut output = Vec::new();
1841
1842 ciborium::ser::into_writer(&cbor, &mut output)
1843 .map_err(|e| ProtocolError::PlatformSerializationError(e.to_string()))?;
1844 Ok(output)
1845 }
1846
1847 #[cfg(any(feature = "server", feature = "verify"))]
1848 pub fn start_at_document_path_and_key(&self, starts_at: &[u8; 32]) -> (Vec<Vec<u8>>, Vec<u8>) {
1850 if self.document_type.documents_keep_history() {
1851 let document_holding_path = self.contract.documents_with_history_primary_key_path(
1852 self.document_type.name().as_str(),
1853 starts_at,
1854 );
1855 (
1856 document_holding_path
1857 .into_iter()
1858 .map(|key| key.to_vec())
1859 .collect::<Vec<_>>(),
1860 vec![0],
1861 )
1862 } else {
1863 let document_holding_path = self
1864 .contract
1865 .documents_primary_key_path(self.document_type.name().as_str());
1866 (
1867 document_holding_path
1868 .into_iter()
1869 .map(|key| key.to_vec())
1870 .collect::<Vec<_>>(),
1871 starts_at.to_vec(),
1872 )
1873 }
1874 }
1875
1876 #[cfg(any(feature = "server", feature = "verify"))]
1877 pub fn validate_in_clause_shape(
1888 &self,
1889 platform_version: &PlatformVersion,
1890 ) -> Result<(), Error> {
1891 match platform_version
1892 .drive
1893 .methods
1894 .document
1895 .query
1896 .non_primary_key_path_query
1897 {
1898 0 => {
1899 if self.internal_clauses.in_clauses.len() > 1 {
1900 return Err(Error::Query(QuerySyntaxError::MultipleInClauses(
1901 "There should only be one in clause",
1902 )));
1903 }
1904 Ok(())
1905 }
1906 1 => {
1907 if self.internal_clauses.in_clauses.len() > 1 && self.start_at.is_some() {
1908 return Err(Error::Query(QuerySyntaxError::Unsupported(
1909 "startAt/startAfter is not supported with multiple in clauses".to_string(),
1910 )));
1911 }
1912 Ok(())
1913 }
1914 version => Err(Error::Drive(DriveError::UnknownVersionMismatch {
1915 method: "DriveDocumentQuery::validate_in_clause_shape".to_string(),
1916 known_versions: vec![0, 1],
1917 received: version,
1918 })),
1919 }
1920 }
1921
1922 #[cfg(feature = "server")]
1923 pub fn construct_path_query_operations(
1925 &self,
1926 drive: &Drive,
1927 include_start_at_for_proof: bool,
1928 transaction: TransactionArg,
1929 drive_operations: &mut Vec<LowLevelDriveOperation>,
1930 platform_version: &PlatformVersion,
1931 ) -> Result<PathQuery, Error> {
1932 self.validate_in_clause_shape(platform_version)?;
1933 {
1936 use dpp::data_contract::document_type::accessors::DocumentTypeV2Getters;
1937 if self.document_type.index_only() && self.is_for_primary_key() {
1938 return Err(Error::Query(QuerySyntaxError::Unsupported(
1939 "indexOnly documents cannot be fetched by id: there is no primary-key \
1940 tree; query through one of the type's indexes"
1941 .to_string(),
1942 )));
1943 }
1944 if self.document_type.index_only() && self.start_at.is_some() {
1945 return Err(Error::Query(QuerySyntaxError::Unsupported(
1946 "startAt/startAfter cursors cannot address an indexOnly position (the \
1947 synthesized document id is a one-way hash of it); paginate with a \
1948 range clause on the terminal property instead — equality clauses on \
1949 the index's properties, `terminal > <last seen value>` ordered by the \
1950 terminal, and a limit"
1951 .to_string(),
1952 )));
1953 }
1954 }
1955 let drive_version = &platform_version.drive;
1956 let document_type_path = self
1958 .contract
1959 .document_type_path(self.document_type.name().as_str())
1960 .into_iter()
1961 .map(|a| a.to_vec())
1962 .collect::<Vec<Vec<u8>>>();
1963
1964 {
1969 use dpp::data_contract::document_type::accessors::DocumentTypeV2Getters;
1970 if self.document_type.index_only() {
1971 if let Some(path_query) =
1972 self.index_only_route(&document_type_path, platform_version)?
1973 {
1974 return Ok(path_query);
1975 }
1976 }
1977 }
1978
1979 let cursor_included = self.start_at_included || self.pads_cursor_page(platform_version);
1980 let (starts_at_document, start_at_path_query) = match &self.start_at {
1981 None => Ok((None, None)),
1982 Some(starts_at) => {
1983 let (start_at_document_path, start_at_document_key) =
1987 self.start_at_document_path_and_key(starts_at);
1988 let start_at_document = drive
1989 .grove_get(
1990 start_at_document_path.as_slice().into(),
1991 &start_at_document_key,
1992 StatefulQuery,
1993 transaction,
1994 drive_operations,
1995 drive_version,
1996 )
1997 .map_err(|e| match e {
1998 Error::GroveDB(e)
1999 if matches!(
2000 e.as_ref(),
2001 GroveError::PathKeyNotFound(_)
2002 | GroveError::PathNotFound(_)
2003 | GroveError::PathParentLayerNotFound(_)
2004 ) =>
2005 {
2006 let error_message = if self.start_at_included {
2007 "startAt document not found"
2008 } else {
2009 "startAfter document not found"
2010 };
2011
2012 Error::Query(QuerySyntaxError::StartDocumentNotFound(error_message))
2013 }
2014 _ => e,
2015 })?
2016 .ok_or(Error::Drive(DriveError::CorruptedCodeExecution(
2017 "expected a value",
2018 )))?;
2019
2020 let path_query =
2021 PathQuery::new_single_key(start_at_document_path, start_at_document_key);
2022
2023 if let Element::Item(item, _) = start_at_document {
2024 let document = Document::from_bytes(
2025 item.as_slice(),
2026 self.document_type,
2027 platform_version,
2028 )?;
2029 Ok((Some((document, cursor_included)), Some(path_query)))
2030 } else {
2031 Err(Error::Drive(DriveError::CorruptedDocumentPath(
2032 "Holding paths should only have items",
2033 )))
2034 }
2035 }
2036 }?;
2037 let mut main_path_query = if self.is_for_primary_key() {
2038 self.get_primary_key_path_query(
2039 document_type_path,
2040 starts_at_document,
2041 platform_version,
2042 )
2043 } else {
2044 self.get_non_primary_key_path_query(
2045 document_type_path,
2046 starts_at_document,
2047 platform_version,
2048 )
2049 }?;
2050 self.pad_cursor_page_limit(&mut main_path_query, platform_version)?;
2051 if !include_start_at_for_proof {
2052 return Ok(main_path_query);
2053 }
2054
2055 if let Some(mut start_at_path_query) = start_at_path_query {
2056 start_at_path_query.query.query.left_to_right =
2062 main_path_query.query.query.left_to_right;
2063 let limit = main_path_query.query.limit.take();
2064 let mut merged = PathQuery::merge(
2065 vec![&start_at_path_query, &main_path_query],
2066 &platform_version.drive.grove_version,
2067 )
2068 .map_err(Error::from)?;
2069 let cursor_on_page_layer = merged.path == main_path_query.path;
2078 let cursor_key_on_page_layer: Option<&[u8]> = if !cursor_on_page_layer {
2082 None
2083 } else if let Some(component) = start_at_path_query.path.get(merged.path.len()) {
2084 Some(component.as_slice())
2085 } else {
2086 match start_at_path_query.query.query.items.as_slice() {
2087 [QueryItem::Key(cursor_key)] => Some(cursor_key.as_slice()),
2088 _ => None,
2089 }
2090 };
2091 let cursor_row_in_page = cursor_key_on_page_layer.is_some_and(|cursor_key| {
2098 main_path_query
2099 .query
2100 .query
2101 .items
2102 .iter()
2103 .any(|item| item.contains(cursor_key))
2104 });
2105 merged.query.limit = limit.map(|a| {
2106 if cursor_row_in_page {
2107 a
2108 } else {
2109 a.saturating_add(1)
2110 }
2111 });
2112 if !cursor_on_page_layer {
2113 merged.query.query.left_to_right = true;
2135 }
2136 Ok(merged)
2137 } else {
2138 Ok(main_path_query)
2139 }
2140 }
2141
2142 #[cfg(any(feature = "server", feature = "verify"))]
2143 pub fn construct_path_query(
2145 &self,
2146 starts_at_document: Option<Document>,
2147 platform_version: &PlatformVersion,
2148 ) -> Result<PathQuery, Error> {
2149 self.validate_in_clause_shape(platform_version)?;
2150 {
2153 use dpp::data_contract::document_type::accessors::DocumentTypeV2Getters;
2154 if self.document_type.index_only() && self.is_for_primary_key() {
2155 return Err(Error::Query(QuerySyntaxError::Unsupported(
2156 "indexOnly documents cannot be fetched by id: there is no primary-key \
2157 tree; query through one of the type's indexes"
2158 .to_string(),
2159 )));
2160 }
2161 if self.document_type.index_only() && self.start_at.is_some() {
2162 return Err(Error::Query(QuerySyntaxError::Unsupported(
2163 "startAt/startAfter cursors cannot address an indexOnly position (the \
2164 synthesized document id is a one-way hash of it); paginate with a \
2165 range clause on the terminal property instead — equality clauses on \
2166 the index's properties, `terminal > <last seen value>` ordered by the \
2167 terminal, and a limit"
2168 .to_string(),
2169 )));
2170 }
2171 }
2172 let document_type_path = self
2174 .contract
2175 .document_type_path(self.document_type.name().as_str())
2176 .into_iter()
2177 .map(|a| a.to_vec())
2178 .collect::<Vec<Vec<u8>>>();
2179
2180 {
2184 use dpp::data_contract::document_type::accessors::DocumentTypeV2Getters;
2185 if self.document_type.index_only() {
2186 if let Some(path_query) =
2187 self.index_only_route(&document_type_path, platform_version)?
2188 {
2189 return Ok(path_query);
2190 }
2191 }
2192 }
2193
2194 let cursor_included = self.start_at_included || self.pads_cursor_page(platform_version);
2195 let starts_at_document =
2196 starts_at_document.map(|starts_at_document| (starts_at_document, cursor_included));
2197 let mut path_query = if self.is_for_primary_key() {
2198 self.get_primary_key_path_query(
2199 document_type_path,
2200 starts_at_document,
2201 platform_version,
2202 )
2203 } else {
2204 self.get_non_primary_key_path_query(
2205 document_type_path,
2206 starts_at_document,
2207 platform_version,
2208 )
2209 }?;
2210 self.pad_cursor_page_limit(&mut path_query, platform_version)?;
2211 Ok(path_query)
2212 }
2213
2214 #[cfg(any(feature = "server", feature = "verify"))]
2215 pub fn pads_cursor_page(&self, platform_version: &PlatformVersion) -> bool {
2231 use dpp::data_contract::document_type::accessors::DocumentTypeV2Getters;
2232 self.start_at.is_some()
2233 && !self.start_at_included
2234 && !self.is_for_primary_key()
2235 && !self.document_type.index_only()
2236 && platform_version
2237 .drive
2238 .methods
2239 .document
2240 .query
2241 .non_primary_key_path_query
2242 >= 1
2243 }
2244
2245 #[cfg(any(feature = "server", feature = "verify"))]
2246 fn pad_cursor_page_limit(
2252 &self,
2253 path_query: &mut PathQuery,
2254 platform_version: &PlatformVersion,
2255 ) -> Result<(), Error> {
2256 if !self.pads_cursor_page(platform_version) {
2257 return Ok(());
2258 }
2259 let offset = path_query.query.offset.take().unwrap_or(0);
2260 if let Some(limit) = path_query.query.limit {
2261 let padded = limit
2262 .checked_add(1)
2263 .and_then(|limit| limit.checked_add(offset))
2264 .ok_or_else(|| {
2265 Error::Query(QuerySyntaxError::InvalidLimit(format!(
2266 "limit {limit} and offset {offset} are too large together with a \
2267 startAfter cursor"
2268 )))
2269 })?;
2270 path_query.query.limit = Some(padded);
2271 }
2272 Ok(())
2273 }
2274
2275 #[cfg(any(feature = "server", feature = "verify"))]
2276 pub(crate) fn strip_cursor_from_page(
2285 &self,
2286 mut serialized_documents: Vec<Vec<u8>>,
2287 platform_version: &PlatformVersion,
2288 ) -> Result<(Vec<Vec<u8>>, u16), Error> {
2289 use dpp::document::serialization_traits::DocumentPlatformConversionMethodsV0;
2290 use dpp::document::DocumentV0Getters;
2291 if !self.pads_cursor_page(platform_version) {
2292 return Ok((serialized_documents, 0));
2293 }
2294 let Some(start_at) = self.start_at else {
2295 return Ok((serialized_documents, 0));
2296 };
2297 if let Some(first) = serialized_documents.first() {
2298 let document = Document::from_bytes(first, self.document_type, platform_version)?;
2299 if document.id().to_buffer() == start_at {
2300 serialized_documents.remove(0);
2301 }
2302 }
2303 let skipped = (self.offset.unwrap_or(0) as usize).min(serialized_documents.len());
2304 serialized_documents.drain(..skipped);
2305 if let Some(limit) = self.limit {
2306 serialized_documents.truncate(limit as usize);
2307 }
2308 Ok((serialized_documents, skipped as u16))
2309 }
2310
2311 #[cfg(feature = "server")]
2312 fn strip_cursor_from_elements(
2314 &self,
2315 mut elements: QueryResultElements,
2316 platform_version: &PlatformVersion,
2317 ) -> Result<(QueryResultElements, u16), Error> {
2318 use dpp::document::DocumentV0Getters;
2319 use grovedb::query_result_type::QueryResultElement;
2320 if !self.pads_cursor_page(platform_version) {
2321 return Ok((elements, 0));
2322 }
2323 let Some(start_at) = self.start_at else {
2324 return Ok((elements, 0));
2325 };
2326 let first_element = match elements.elements.first() {
2327 Some(QueryResultElement::ElementResultItem(element))
2328 | Some(QueryResultElement::KeyElementPairResultItem((_, element)))
2329 | Some(QueryResultElement::PathKeyElementTrioResultItem((_, _, element))) => {
2330 Some(element)
2331 }
2332 None => None,
2333 };
2334 let first_is_cursor = match first_element {
2335 Some(Element::Item(bytes, _)) => {
2336 Document::from_bytes(bytes, self.document_type, platform_version)?
2337 .id()
2338 .to_buffer()
2339 == start_at
2340 }
2341 _ => false,
2342 };
2343 if first_is_cursor {
2344 elements.elements.remove(0);
2345 }
2346 let skipped = (self.offset.unwrap_or(0) as usize).min(elements.elements.len());
2347 elements.elements.drain(..skipped);
2348 if let Some(limit) = self.limit {
2349 elements.elements.truncate(limit as usize);
2350 }
2351 Ok((elements, skipped as u16))
2352 }
2353
2354 #[cfg(any(feature = "server", feature = "verify"))]
2355 pub fn get_primary_key_path_query(
2357 &self,
2358 document_type_path: Vec<Vec<u8>>,
2359 starts_at_document: Option<(Document, bool)>,
2360 platform_version: &PlatformVersion,
2361 ) -> Result<PathQuery, Error> {
2362 let mut path = document_type_path;
2363
2364 path.push(vec![0]);
2366
2367 if let Some(primary_key_equal_clause) = &self.internal_clauses.primary_key_equal_clause {
2368 let mut query = Query::new();
2369 let key = self.document_type.serialize_value_for_key(
2370 "$id",
2371 &primary_key_equal_clause.value,
2372 platform_version,
2373 )?;
2374 query.insert_key(key);
2375
2376 if self.document_type.documents_keep_history() {
2377 if let Some(block_time) = self.block_time_ms {
2379 let encoded_block_time = encode_u64(block_time);
2380 let mut sub_query = Query::new_with_direction(false);
2381 sub_query.insert_range_to_inclusive(..=encoded_block_time);
2382 query.set_subquery(sub_query);
2383 } else {
2384 query.set_subquery_key(vec![0]);
2385 }
2386 }
2387
2388 Ok(PathQuery::new(path, SizedQuery::new(query, Some(1), None)))
2389 } else {
2390 let left_to_right = if self.order_by.keys().len() == 1 {
2392 if self.order_by.keys().next().unwrap() != "$id" {
2393 return Err(Error::Query(QuerySyntaxError::InvalidOrderByProperties(
2394 "order by should include $id only",
2395 )));
2396 }
2397
2398 let order_clause = self.order_by.get("$id").unwrap();
2399
2400 order_clause.ascending
2401 } else {
2402 true
2403 };
2404
2405 let mut query = Query::new_with_direction(left_to_right);
2406 let starts_at_key_option = match starts_at_document {
2409 None => None,
2410 Some((document, included)) => {
2411 document
2413 .get_raw_for_document_type(
2414 "$id",
2415 self.document_type,
2416 None,
2417 platform_version,
2418 )?
2419 .map(|raw_value_option| (raw_value_option, included))
2420 }
2421 };
2422
2423 if let Some(primary_key_in_clause) = &self.internal_clauses.primary_key_in_clause {
2424 let in_values = primary_key_in_clause.in_values().into_data_with_error()??;
2425
2426 match starts_at_key_option {
2427 None => {
2428 for value in in_values.iter() {
2429 let key = self.document_type.serialize_value_for_key(
2430 "$id",
2431 value,
2432 platform_version,
2433 )?;
2434 query.insert_key(key)
2435 }
2436 }
2437 Some((starts_at_key, included)) => {
2438 for value in in_values.iter() {
2439 let key = self.document_type.serialize_value_for_key(
2440 "$id",
2441 value,
2442 platform_version,
2443 )?;
2444
2445 if (left_to_right && starts_at_key < key)
2446 || (!left_to_right && starts_at_key > key)
2447 || (included && starts_at_key == key)
2448 {
2449 query.insert_key(key);
2450 }
2451 }
2452 }
2453 }
2454
2455 if self.document_type.documents_keep_history() {
2456 if let Some(_block_time) = self.block_time_ms {
2458 return Err(Error::Query(QuerySyntaxError::Unsupported(
2460 "Not yet implemented".to_string(),
2461 )));
2462 } else {
2470 query.set_subquery_key(vec![0]);
2471 }
2472 }
2473
2474 Ok(PathQuery::new(
2475 path,
2476 SizedQuery::new(query, self.limit, self.offset),
2477 ))
2478 } else {
2479 match starts_at_key_option {
2481 None => {
2482 query.insert_all();
2483 }
2484 Some((starts_at_key, included)) => match left_to_right {
2485 true => match included {
2486 true => query.insert_range_from(starts_at_key..),
2487 false => query.insert_range_after(starts_at_key..),
2488 },
2489 false => match included {
2490 true => query.insert_range_to_inclusive(..=starts_at_key),
2491 false => query.insert_range_to(..starts_at_key),
2492 },
2493 },
2494 }
2495
2496 if self.document_type.documents_keep_history() {
2497 if let Some(_block_time) = self.block_time_ms {
2499 return Err(Error::Query(QuerySyntaxError::Unsupported(
2500 "this query is not supported".to_string(),
2501 )));
2502 } else {
2510 query.set_subquery_key(vec![0]);
2511 }
2512 }
2513
2514 Ok(PathQuery::new(
2515 path,
2516 SizedQuery::new(query, self.limit, self.offset),
2517 ))
2518 }
2519 }
2520 }
2521
2522 #[cfg(any(feature = "server", feature = "verify"))]
2523 pub fn find_best_index(&self, platform_version: &PlatformVersion) -> Result<&Index, Error> {
2538 match self.select_best_index(platform_version)? {
2539 BestIndexOutcome::Matched(index) => Ok(index),
2540 BestIndexOutcome::NoIndexMatches(no_index_error) => Err(no_index_error),
2541 }
2542 }
2543
2544 pub(crate) fn select_best_index(
2553 &self,
2554 platform_version: &PlatformVersion,
2555 ) -> Result<BestIndexOutcome<'_>, Error> {
2556 if self.resolved_time_ranges.len() > 1 {
2562 return Err(Error::Query(QuerySyntaxError::Unsupported(format!(
2563 "at most one time-range selection (IN_TIME_RANGE) is supported per query; this \
2564 one resolves {:?}, and no single index can bucket more than one field",
2565 self.resolved_time_ranges
2566 ))));
2567 }
2568
2569 self.validate_resolved_source_shape()?;
2573
2574 if self.internal_clauses.in_clauses.len() > 1 {
2575 return Ok(BestIndexOutcome::Matched(
2579 self.find_best_index_for_multiple_in_clauses()?.0,
2580 ));
2581 }
2582
2583 let equal_fields = self
2584 .internal_clauses
2585 .equal_clauses
2586 .keys()
2587 .map(|s| s.as_str())
2588 .collect::<Vec<&str>>();
2589 let in_field = self
2590 .internal_clauses
2591 .in_clauses
2592 .first()
2593 .map(|in_clause| in_clause.field.as_str());
2594 let range_field = self
2595 .internal_clauses
2596 .range_clause
2597 .as_ref()
2598 .map(|range_clause| range_clause.field.as_str());
2599 let order_by_keys: Vec<&str> = self.order_by.keys().map(String::as_str).collect();
2600
2601 let mut bound_fields = equal_fields.clone();
2608 bound_fields.extend(range_field);
2609 bound_fields.extend(in_field);
2610 for order_by_key in &order_by_keys {
2611 if !bound_fields.contains(order_by_key) {
2612 bound_fields.push(order_by_key);
2613 }
2614 }
2615
2616 let Some((index, difference)) = self.document_type.index_for_types_matching(
2617 equal_fields.as_slice(),
2618 range_field,
2619 in_field,
2620 order_by_keys.as_slice(),
2621 |index| {
2622 index_admissible_for_resolved_time_range(index, &self.resolved_time_ranges)
2623 && index_admissible_for_skip_if_absent(index, &bound_fields)
2624 },
2625 platform_version,
2626 )?
2627 else {
2628 return Ok(BestIndexOutcome::NoIndexMatches(
2629 match self.resolved_time_ranges.first() {
2630 Some(resolved) => {
2637 Error::Query(QuerySyntaxError::WhereClauseOnNonIndexedProperty(format!(
2638 "a time-range query on \"{}\" requires an index that buckets it with \
2639 the resolved grid AND covers the query's other where and order-by \
2640 fields; valid indexes are: {:?}",
2641 resolved.field(),
2642 self.document_type.indexes()
2643 )))
2644 }
2645 None => {
2646 let has_bucketed_index = self
2652 .document_type
2653 .indexes()
2654 .values()
2655 .any(|index| index.time_range.is_some());
2656 if has_bucketed_index {
2657 Error::Query(QuerySyntaxError::WhereClauseOnNonIndexedProperty(
2658 format!(
2659 "query must be for valid indexes, valid indexes are: {:?}; note: \
2660 this document type's time-range (timeRange) indexes only serve \
2661 IN_TIME_RANGE selections carrying their resolution — a raw clause \
2662 on the bucketed field never binds to them",
2663 self.document_type.indexes()
2664 ),
2665 ))
2666 } else {
2667 Error::Query(QuerySyntaxError::WhereClauseOnNonIndexedProperty(
2668 format!(
2669 "query must be for valid indexes, valid indexes are: {:?}",
2670 self.document_type.indexes()
2671 ),
2672 ))
2673 }
2674 }
2675 },
2676 ));
2677 };
2678 if difference > defaults::MAX_INDEX_DIFFERENCE {
2679 return Ok(BestIndexOutcome::NoIndexMatches(Error::Query(
2680 QuerySyntaxError::QueryTooFarFromIndex("query must better match an existing index"),
2681 )));
2682 }
2683
2684 Ok(BestIndexOutcome::Matched(index))
2691 }
2692
2693 #[cfg(any(feature = "server", feature = "verify"))]
2708 pub(crate) fn validate_resolved_source_shape(&self) -> Result<(), Error> {
2709 let Some(source) = self
2710 .resolved_time_ranges
2711 .first()
2712 .map(|resolved| resolved.field())
2713 else {
2714 return Ok(());
2715 };
2716 let has_equality_on_source = self.internal_clauses.equal_clauses.contains_key(source);
2717 let range_or_in_on_source = self
2718 .internal_clauses
2719 .range_clause
2720 .as_ref()
2721 .is_some_and(|clause| clause.field == source)
2722 || self
2723 .internal_clauses
2724 .in_clauses
2725 .iter()
2726 .any(|clause| clause.field == source);
2727 if !has_equality_on_source || range_or_in_on_source || self.order_by.contains_key(source) {
2728 return Err(Error::Query(QuerySyntaxError::Unsupported(format!(
2729 "the index on \"{source}\" buckets it into time ranges: it can only be queried \
2730 through a time-range selection (IN_TIME_RANGE, which resolves to an exact \
2731 bucket equality), not with ranges, IN, or ordering on that property"
2732 ))));
2733 }
2734 Ok(())
2735 }
2736
2737 #[cfg(any(feature = "server", feature = "verify"))]
2738 pub fn query_item_for_starts_at_key(starts_at_key: Vec<u8>, left_to_right: bool) -> QueryItem {
2740 if left_to_right {
2741 QueryItem::RangeAfter(starts_at_key..)
2742 } else {
2743 QueryItem::RangeTo(..starts_at_key)
2744 }
2745 }
2746
2747 #[cfg(any(feature = "server", feature = "verify"))]
2748 pub fn get_non_primary_key_path_query(
2755 &self,
2756 document_type_path: Vec<Vec<u8>>,
2757 starts_at_document: Option<(Document, bool)>,
2758 platform_version: &PlatformVersion,
2759 ) -> Result<PathQuery, Error> {
2760 match platform_version
2761 .drive
2762 .methods
2763 .document
2764 .query
2765 .non_primary_key_path_query
2766 {
2767 0 => self.get_non_primary_key_path_query_v0(
2768 document_type_path,
2769 starts_at_document,
2770 platform_version,
2771 ),
2772 1 => self.get_non_primary_key_path_query_v1(
2773 document_type_path,
2774 starts_at_document,
2775 platform_version,
2776 ),
2777 version => Err(Error::Drive(DriveError::UnknownVersionMismatch {
2778 method: "DriveDocumentQuery::get_non_primary_key_path_query".to_string(),
2779 known_versions: vec![0, 1],
2780 received: version,
2781 })),
2782 }
2783 }
2784
2785 #[cfg(feature = "server")]
2786 pub fn execute_with_proof(
2788 self,
2789 drive: &Drive,
2790 block_info: Option<BlockInfo>,
2791 transaction: TransactionArg,
2792 platform_version: &PlatformVersion,
2793 ) -> Result<(Vec<u8>, u64), Error> {
2794 self.ensure_no_sub_queries("execute_with_proof")?;
2795 let mut drive_operations = vec![];
2796 let items = self.execute_with_proof_internal(
2797 drive,
2798 transaction,
2799 &mut drive_operations,
2800 platform_version,
2801 )?;
2802 let cost = if let Some(block_info) = block_info {
2803 let fee_result = Drive::calculate_fee(
2804 None,
2805 Some(drive_operations),
2806 &block_info.epoch,
2807 drive.config.epochs_per_era,
2808 platform_version,
2809 None,
2810 )?;
2811 fee_result.processing_fee
2812 } else {
2813 0
2814 };
2815 Ok((items, cost))
2816 }
2817
2818 #[cfg(feature = "server")]
2819 pub(crate) fn execute_with_proof_internal(
2821 self,
2822 drive: &Drive,
2823 transaction: TransactionArg,
2824 drive_operations: &mut Vec<LowLevelDriveOperation>,
2825 platform_version: &PlatformVersion,
2826 ) -> Result<Vec<u8>, Error> {
2827 let path_query = self.construct_path_query_operations(
2828 drive,
2829 true,
2830 transaction,
2831 drive_operations,
2832 platform_version,
2833 )?;
2834 drive.grove_get_proved_path_query(
2835 &path_query,
2836 transaction,
2837 drive_operations,
2838 &platform_version.drive,
2839 )
2840 }
2841
2842 #[cfg(all(feature = "server", feature = "verify"))]
2843 pub fn execute_with_proof_only_get_elements(
2845 self,
2846 drive: &Drive,
2847 block_info: Option<BlockInfo>,
2848 transaction: TransactionArg,
2849 platform_version: &PlatformVersion,
2850 ) -> Result<(RootHash, Vec<Vec<u8>>, u64), Error> {
2851 self.ensure_no_sub_queries("execute_with_proof_only_get_elements")?;
2852 let mut drive_operations = vec![];
2853 let (root_hash, items) = self.execute_with_proof_only_get_elements_internal(
2854 drive,
2855 transaction,
2856 &mut drive_operations,
2857 platform_version,
2858 )?;
2859 let cost = if let Some(block_info) = block_info {
2860 let fee_result = Drive::calculate_fee(
2861 None,
2862 Some(drive_operations),
2863 &block_info.epoch,
2864 drive.config.epochs_per_era,
2865 platform_version,
2866 None,
2867 )?;
2868 fee_result.processing_fee
2869 } else {
2870 0
2871 };
2872 Ok((root_hash, items, cost))
2873 }
2874
2875 #[cfg(all(feature = "server", feature = "verify"))]
2876 pub(crate) fn execute_with_proof_only_get_elements_internal(
2878 self,
2879 drive: &Drive,
2880 transaction: TransactionArg,
2881 drive_operations: &mut Vec<LowLevelDriveOperation>,
2882 platform_version: &PlatformVersion,
2883 ) -> Result<(RootHash, Vec<Vec<u8>>), Error> {
2884 let path_query = self.construct_path_query_operations(
2885 drive,
2886 true,
2887 transaction,
2888 drive_operations,
2889 platform_version,
2890 )?;
2891
2892 let proof = drive.grove_get_proved_path_query(
2893 &path_query,
2894 transaction,
2895 drive_operations,
2896 &platform_version.drive,
2897 )?;
2898 self.verify_proof_keep_serialized(proof.as_slice(), platform_version)
2899 }
2900
2901 #[cfg(feature = "server")]
2902 pub fn execute_raw_results_no_proof(
2904 &self,
2905 drive: &Drive,
2906 block_info: Option<BlockInfo>,
2907 transaction: TransactionArg,
2908 platform_version: &PlatformVersion,
2909 ) -> Result<(Vec<Vec<u8>>, u16, u64), Error> {
2910 self.ensure_no_sub_queries("execute_raw_results_no_proof")?;
2911 let mut drive_operations = vec![];
2912 let (items, skipped) = self.execute_raw_results_no_proof_internal(
2913 drive,
2914 transaction,
2915 &mut drive_operations,
2916 platform_version,
2917 )?;
2918 let cost = if let Some(block_info) = block_info {
2919 let fee_result = Drive::calculate_fee(
2920 None,
2921 Some(drive_operations),
2922 &block_info.epoch,
2923 drive.config.epochs_per_era,
2924 platform_version,
2925 None,
2926 )?;
2927 fee_result.processing_fee
2928 } else {
2929 0
2930 };
2931 Ok((items, skipped, cost))
2932 }
2933
2934 #[cfg(feature = "server")]
2935 pub(crate) fn execute_raw_results_no_proof_internal(
2937 &self,
2938 drive: &Drive,
2939 transaction: TransactionArg,
2940 drive_operations: &mut Vec<LowLevelDriveOperation>,
2941 platform_version: &PlatformVersion,
2942 ) -> Result<(Vec<Vec<u8>>, u16), Error> {
2943 {
2956 use dpp::data_contract::document_type::accessors::DocumentTypeV2Getters;
2957 if self.document_type.index_only() {
2958 if !self.is_for_primary_key() && self.start_at.is_none() {
2963 let index = self.index_only_query_index(platform_version)?;
2964 let covers_every_property = self
2965 .document_type
2966 .flattened_properties()
2967 .iter()
2968 .filter(|(_, property)| {
2969 !matches!(property.property_type, DocumentPropertyType::Object(_))
2970 })
2971 .all(|(name, _)| {
2972 index.terminal.as_deref() == Some(name.as_str())
2973 || index
2974 .properties
2975 .iter()
2976 .any(|index_property| index_property.name == *name)
2977 });
2978 if !covers_every_property {
2979 return Err(Error::Query(QuerySyntaxError::Unsupported(
2980 "this indexOnly query's index does not cover every property, so \
2981 the documents it synthesizes cannot be serialized into a \
2982 non-proof response; query through an index covering all \
2983 properties, or use a proved query"
2984 .to_string(),
2985 )));
2986 }
2987 }
2988 let (documents, skipped) = self.execute_index_only_documents_no_proof_internal(
2989 drive,
2990 transaction,
2991 drive_operations,
2992 platform_version,
2993 )?;
2994 let serialized = documents
2995 .into_iter()
2996 .map(|document| {
2997 document
2998 .serialize(self.document_type, self.contract, platform_version)
2999 .map_err(|error| match error {
3000 ProtocolError::DataContractError(
3001 dpp::data_contract::errors::DataContractError::MissingRequiredKey(_),
3002 ) => Error::Query(QuerySyntaxError::Unsupported(
3003 "this indexOnly query's index does not cover every required \
3004 property, so the documents it synthesizes cannot be \
3005 serialized into a non-proof response; query through an \
3006 index covering all properties, or use a proved query"
3007 .to_string(),
3008 )),
3009 other => other.into(),
3010 })
3011 })
3012 .collect::<Result<Vec<_>, Error>>()?;
3013 return Ok((serialized, skipped));
3014 }
3015 }
3016
3017 let path_query = self.construct_path_query_operations(
3018 drive,
3019 false,
3020 transaction,
3021 drive_operations,
3022 platform_version,
3023 )?;
3024
3025 let query_result = drive.grove_get_path_query_serialized_results(
3026 &path_query,
3027 transaction,
3028 drive_operations,
3029 &platform_version.drive,
3030 );
3031 match query_result {
3032 Err(Error::GroveDB(e))
3033 if matches!(
3034 e.as_ref(),
3035 GroveError::PathKeyNotFound(_)
3036 | GroveError::PathNotFound(_)
3037 | GroveError::PathParentLayerNotFound(_)
3038 ) =>
3039 {
3040 Ok((Vec::new(), 0))
3041 }
3042 _ => {
3043 let (data, skipped) = query_result?;
3044 let (data, cursor_skipped) = self.strip_cursor_from_page(data, platform_version)?;
3045 Ok((data, skipped.saturating_add(cursor_skipped)))
3046 }
3047 }
3048 }
3049
3050 #[cfg(feature = "server")]
3051 pub(crate) fn execute_no_proof_internal(
3053 &self,
3054 drive: &Drive,
3055 result_type: QueryResultType,
3056 transaction: TransactionArg,
3057 drive_operations: &mut Vec<LowLevelDriveOperation>,
3058 platform_version: &PlatformVersion,
3059 ) -> Result<(QueryResultElements, u16), Error> {
3060 let path_query = self.construct_path_query_operations(
3061 drive,
3062 false,
3063 transaction,
3064 drive_operations,
3065 platform_version,
3066 )?;
3067 let query_result = drive.grove_get_path_query(
3068 &path_query,
3069 transaction,
3070 result_type,
3071 drive_operations,
3072 &platform_version.drive,
3073 );
3074 match query_result {
3075 Err(Error::GroveDB(e))
3076 if matches!(
3077 e.as_ref(),
3078 GroveError::PathKeyNotFound(_)
3079 | GroveError::PathNotFound(_)
3080 | GroveError::PathParentLayerNotFound(_)
3081 ) =>
3082 {
3083 Ok((QueryResultElements::new(), 0))
3084 }
3085 _ => {
3086 let (data, skipped) = query_result?;
3087 let (data, cursor_skipped) =
3088 self.strip_cursor_from_elements(data, platform_version)?;
3089 Ok((data, skipped.saturating_add(cursor_skipped)))
3090 }
3091 }
3092 }
3093}
3094
3095impl<'a> From<&DriveDocumentQuery<'a>> for BTreeMap<String, Value> {
3097 fn from(query: &DriveDocumentQuery<'a>) -> Self {
3098 let mut response = BTreeMap::<String, Value>::new();
3099
3100 response.insert(
3103 "contract_id".to_string(),
3104 Value::Identifier(query.contract.id().to_buffer()),
3105 );
3106
3107 response.insert(
3110 "document_type_name".to_string(),
3111 Value::Text(query.document_type.name().to_string()),
3112 );
3113
3114 let all_where_clauses: Vec<WhereClause> = query.internal_clauses.clone().into();
3116 response.insert(
3117 "where".to_string(),
3118 Value::Array(all_where_clauses.into_iter().map(|v| v.into()).collect()),
3119 );
3120
3121 if let Some(offset) = query.offset {
3123 response.insert("offset".to_string(), Value::U16(offset));
3124 };
3125 if let Some(limit) = query.limit {
3127 response.insert("limit".to_string(), Value::U16(limit));
3128 };
3129 let order_by = &query.order_by;
3131 let value: Vec<Value> = order_by
3132 .into_iter()
3133 .map(|(_k, v)| v.clone().into())
3134 .collect();
3135 response.insert("orderBy".to_string(), Value::Array(value));
3136
3137 if let Some(start_at) = query.start_at {
3139 let v = Value::Identifier(start_at);
3140 if query.start_at_included {
3141 response.insert("startAt".to_string(), v);
3142 } else {
3143 response.insert("startAfter".to_string(), v);
3144 }
3145 };
3146
3147 if let Some(block_time_ms) = query.block_time_ms {
3149 response.insert("blockTime".to_string(), Value::U64(block_time_ms));
3150 };
3151
3152 response
3153 }
3154}
3155
3156#[cfg(feature = "server")]
3157#[cfg(test)]
3158mod tests {
3159
3160 use dpp::data_contract::document_type::accessors::DocumentTypeV0Getters;
3161
3162 use dpp::prelude::Identifier;
3163 use grovedb::Query;
3164 use indexmap::IndexMap;
3165 use rand::prelude::StdRng;
3166 use rand::SeedableRng;
3167 use serde_json::json;
3168 use std::borrow::Cow;
3169 use std::collections::BTreeMap;
3170 use std::option::Option::None;
3171 use tempfile::TempDir;
3172
3173 use crate::drive::Drive;
3174 use crate::query::{
3175 DriveDocumentQuery, InternalClauses, OrderClause, WhereClause, WhereOperator,
3176 };
3177 use crate::util::storage_flags::StorageFlags;
3178
3179 use dpp::data_contract::DataContract;
3180
3181 use serde_json::Value::Null;
3182
3183 use crate::config::DriveConfig;
3184 use crate::util::test_helpers::setup::setup_drive_with_initial_state_structure;
3185 use dpp::block::block_info::BlockInfo;
3186 use dpp::data_contract::accessors::v0::DataContractV0Getters;
3187 use dpp::data_contracts::SystemDataContract;
3188 use dpp::document::DocumentV0;
3189 use dpp::platform_value::string_encoding::Encoding;
3190 use dpp::platform_value::Value;
3191 use dpp::system_data_contracts::load_system_data_contract;
3192 use dpp::tests::fixtures::{get_data_contract_fixture, get_dpns_data_contract_fixture};
3193 use dpp::tests::json_document::json_document_to_contract;
3194 use dpp::util::cbor_serializer;
3195 use dpp::version::PlatformVersion;
3196
3197 fn setup_family_contract() -> (Drive, DataContract) {
3198 let tmp_dir = TempDir::new().unwrap();
3199
3200 let platform_version = PlatformVersion::latest();
3201
3202 let (drive, _) = Drive::open(tmp_dir, None).expect("expected to open Drive successfully");
3203
3204 drive
3205 .create_initial_state_structure(None, platform_version)
3206 .expect("expected to create root tree successfully");
3207
3208 let contract_path = "tests/supporting_files/contract/family/family-contract.json";
3209
3210 let contract = json_document_to_contract(contract_path, false, platform_version)
3212 .expect("expected to get document");
3213
3214 let storage_flags = Some(Cow::Owned(StorageFlags::SingleEpoch(0)));
3215 drive
3216 .apply_contract(
3217 &contract,
3218 BlockInfo::default(),
3219 true,
3220 storage_flags,
3221 None,
3222 platform_version,
3223 )
3224 .expect("expected to apply contract successfully");
3225
3226 (drive, contract)
3227 }
3228
3229 fn setup_withdrawal_contract() -> (Drive, DataContract) {
3230 let tmp_dir = TempDir::new().unwrap();
3231
3232 let platform_version = PlatformVersion::latest();
3233
3234 let (drive, _) = Drive::open(tmp_dir, None).expect("expected to open Drive successfully");
3235
3236 drive
3237 .create_initial_state_structure(None, platform_version)
3238 .expect("expected to create root tree successfully");
3239
3240 let contract = load_system_data_contract(SystemDataContract::Withdrawals, platform_version)
3242 .expect("load system contact");
3243
3244 let storage_flags = Some(Cow::Owned(StorageFlags::SingleEpoch(0)));
3245 drive
3246 .apply_contract(
3247 &contract,
3248 BlockInfo::default(),
3249 true,
3250 storage_flags,
3251 None,
3252 platform_version,
3253 )
3254 .expect("expected to apply contract successfully");
3255
3256 (drive, contract)
3257 }
3258
3259 fn setup_family_birthday_contract() -> (Drive, DataContract) {
3260 let drive = setup_drive_with_initial_state_structure(None);
3261
3262 let platform_version = PlatformVersion::latest();
3263
3264 let contract_path =
3265 "tests/supporting_files/contract/family/family-contract-with-birthday.json";
3266
3267 let contract = json_document_to_contract(contract_path, false, platform_version)
3269 .expect("expected to get document");
3270 let storage_flags = Some(Cow::Owned(StorageFlags::SingleEpoch(0)));
3271 drive
3272 .apply_contract(
3273 &contract,
3274 BlockInfo::default(),
3275 true,
3276 storage_flags,
3277 None,
3278 platform_version,
3279 )
3280 .expect("expected to apply contract successfully");
3281
3282 (drive, contract)
3283 }
3284
3285 #[test]
3286 fn test_drive_query_from_to_cbor() {
3287 let config = DriveConfig::default();
3288 let contract = get_data_contract_fixture(None, 0, 1).data_contract_owned();
3289 let document_type = contract
3290 .document_type_for_name("niceDocument")
3291 .expect("expected to get nice document");
3292 let start_after = Identifier::random();
3293
3294 let query_value = json!({
3295 "contract_id": contract.id(),
3296 "document_type_name": document_type.name(),
3297 "where": [
3298 ["firstName", "<", "Gilligan"],
3299 ["lastName", "=", "Doe"]
3300 ],
3301 "limit": 100u16,
3302 "offset": 10u16,
3303 "orderBy": [
3304 ["firstName", "asc"],
3305 ["lastName", "desc"],
3306 ],
3307 "startAfter": start_after,
3308 "blockTime": 13453432u64,
3309 });
3310
3311 let where_cbor = cbor_serializer::serializable_value_to_cbor(&query_value, None)
3312 .expect("expected to serialize to cbor");
3313 let query = DriveDocumentQuery::from_cbor(
3314 where_cbor.as_slice(),
3315 &contract,
3316 document_type,
3317 &config,
3318 PlatformVersion::latest(),
3319 )
3320 .expect("deserialize cbor shouldn't fail");
3321
3322 let cbor = query.to_cbor().expect("should serialize cbor");
3323
3324 let deserialized = DriveDocumentQuery::from_cbor(
3325 &cbor,
3326 &contract,
3327 document_type,
3328 &config,
3329 PlatformVersion::latest(),
3330 )
3331 .expect("should deserialize cbor");
3332
3333 assert_eq!(query, deserialized);
3334
3335 assert_eq!(deserialized.start_at, Some(start_after.to_buffer()));
3336 assert!(!deserialized.start_at_included);
3337 assert_eq!(deserialized.block_time_ms, Some(13453432u64));
3338 }
3339
3340 #[test]
3341 fn test_invalid_query_ranges_different_fields() {
3342 let query_value = json!({
3343 "where": [
3344 ["firstName", "<", "Gilligan"],
3345 ["lastName", "<", "Michelle"],
3346 ],
3347 "limit": 100,
3348 "orderBy": [
3349 ["firstName", "asc"],
3350 ["lastName", "asc"],
3351 ]
3352 });
3353 let contract = get_data_contract_fixture(None, 0, 1).data_contract_owned();
3354 let document_type = contract
3355 .document_type_for_name("niceDocument")
3356 .expect("expected to get nice document");
3357
3358 let where_cbor = cbor_serializer::serializable_value_to_cbor(&query_value, None)
3359 .expect("expected to serialize to cbor");
3360 DriveDocumentQuery::from_cbor(
3361 where_cbor.as_slice(),
3362 &contract,
3363 document_type,
3364 &DriveConfig::default(),
3365 PlatformVersion::latest(),
3366 )
3367 .expect_err("all ranges must be on same field");
3368 }
3369
3370 #[test]
3371 fn test_invalid_query_extra_invalid_field() {
3372 let query_value = json!({
3373 "where": [
3374 ["firstName", "<", "Gilligan"],
3375 ],
3376 "limit": 100,
3377 "orderBy": [
3378 ["firstName", "asc"],
3379 ["lastName", "asc"],
3380 ],
3381 "invalid": 0,
3382 });
3383 let contract = get_data_contract_fixture(None, 0, 1).data_contract_owned();
3384 let document_type = contract
3385 .document_type_for_name("niceDocument")
3386 .expect("expected to get nice document");
3387
3388 let where_cbor = cbor_serializer::serializable_value_to_cbor(&query_value, None)
3389 .expect("expected to serialize to cbor");
3390 DriveDocumentQuery::from_cbor(
3391 where_cbor.as_slice(),
3392 &contract,
3393 document_type,
3394 &DriveConfig::default(),
3395 PlatformVersion::latest(),
3396 )
3397 .expect_err("fields of queries must of defined supported types (where, limit, orderBy...)");
3398 }
3399
3400 #[test]
3401 fn test_invalid_query_conflicting_clauses() {
3402 let query_value = json!({
3403 "where": [
3404 ["firstName", "<", "Gilligan"],
3405 ["firstName", ">", "Gilligan"],
3406 ],
3407 "limit": 100,
3408 "orderBy": [
3409 ["firstName", "asc"],
3410 ["lastName", "asc"],
3411 ],
3412 });
3413
3414 let contract = get_data_contract_fixture(None, 0, 1).data_contract_owned();
3415 let document_type = contract
3416 .document_type_for_name("niceDocument")
3417 .expect("expected to get nice document");
3418
3419 let where_cbor = cbor_serializer::serializable_value_to_cbor(&query_value, None)
3420 .expect("expected to serialize to cbor");
3421 DriveDocumentQuery::from_cbor(
3422 where_cbor.as_slice(),
3423 &contract,
3424 document_type,
3425 &DriveConfig::default(),
3426 PlatformVersion::latest(),
3427 )
3428 .expect_err("the query should not be created");
3429 }
3430
3431 #[test]
3432 fn test_valid_query_groupable_meeting_clauses() {
3433 let query_value = json!({
3434 "where": [
3435 ["firstName", "<=", "Gilligan"],
3436 ["firstName", ">", "Gilligan"],
3437 ],
3438 "limit": 100,
3439 "orderBy": [
3440 ["firstName", "asc"],
3441 ["lastName", "asc"],
3442 ],
3443 });
3444
3445 let contract = get_data_contract_fixture(None, 0, 1).data_contract_owned();
3446 let document_type = contract
3447 .document_type_for_name("niceDocument")
3448 .expect("expected to get nice document");
3449
3450 let where_cbor = cbor_serializer::serializable_value_to_cbor(&query_value, None)
3451 .expect("expected to serialize to cbor");
3452 DriveDocumentQuery::from_cbor(
3453 where_cbor.as_slice(),
3454 &contract,
3455 document_type,
3456 &DriveConfig::default(),
3457 PlatformVersion::latest(),
3458 )
3459 .expect("the query should be created");
3460 }
3461
3462 #[test]
3463 fn test_valid_query_query_field_at_max_length() {
3464 let long_string = "t".repeat(255);
3465 let query_value = json!({
3466 "where": [
3467 ["firstName", "<", long_string],
3468 ],
3469 "limit": 100,
3470 "orderBy": [
3471 ["firstName", "asc"],
3472 ["lastName", "asc"],
3473 ],
3474 });
3475 let contract = get_data_contract_fixture(None, 0, 1).data_contract_owned();
3476 let document_type = contract
3477 .document_type_for_name("niceDocument")
3478 .expect("expected to get nice document");
3479
3480 let where_cbor = cbor_serializer::serializable_value_to_cbor(&query_value, None)
3481 .expect("expected to serialize to cbor");
3482 DriveDocumentQuery::from_cbor(
3483 where_cbor.as_slice(),
3484 &contract,
3485 document_type,
3486 &DriveConfig::default(),
3487 PlatformVersion::latest(),
3488 )
3489 .expect("query should be fine for a 255 byte long string");
3490 }
3491
3492 #[test]
3493 fn test_valid_query_drive_document_query() {
3494 let platform_version = PlatformVersion::latest();
3495 let mut rng = StdRng::seed_from_u64(5);
3496 let contract =
3497 get_dpns_data_contract_fixture(Some(Identifier::random_with_rng(&mut rng)), 0, 1)
3498 .data_contract_owned();
3499 let domain = contract
3500 .document_type_for_name("domain")
3501 .expect("expected to get domain");
3502
3503 let query_asc = DriveDocumentQuery {
3504 contract: &contract,
3505 document_type: domain,
3506 internal_clauses: InternalClauses {
3507 primary_key_in_clause: None,
3508 primary_key_equal_clause: None,
3509 in_clauses: Vec::new(),
3510 range_clause: Some(WhereClause {
3511 field: "records.identity".to_string(),
3512 operator: WhereOperator::LessThan,
3513 value: Value::Identifier(
3514 Identifier::from_string(
3515 "AYN4srupPWDrp833iG5qtmaAsbapNvaV7svAdncLN5Rh",
3516 Encoding::Base58,
3517 )
3518 .unwrap()
3519 .to_buffer(),
3520 ),
3521 }),
3522 equal_clauses: BTreeMap::new(),
3523 },
3524 offset: None,
3525 limit: Some(6),
3526 order_by: vec![(
3527 "records.identity".to_string(),
3528 OrderClause {
3529 field: "records.identity".to_string(),
3530 ascending: false,
3531 },
3532 )]
3533 .into_iter()
3534 .collect(),
3535 start_at: None,
3536 start_at_included: false,
3537 block_time_ms: None,
3538 resolved_time_ranges: vec![],
3539 sub_queries: vec![],
3540 };
3541
3542 let path_query = query_asc
3543 .construct_path_query(None, platform_version)
3544 .expect("expected to create path query");
3545
3546 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 } }");
3547
3548 let encoded = bincode::encode_to_vec(&path_query, bincode::config::standard())
3550 .expect("Failed to serialize PathQuery");
3551
3552 let hex_string = hex::encode(encoded);
3554
3555 assert_eq!(hex_string, "050140201da29f488023e306ff9a680bc9837153fb0778c8ee9c934a87dc0de1d69abd3c010106646f6d61696e107265636f7264732e6964656e74697479010105208dc201fd7ad7905f8a84d66218e2b387daea7fe4739ae0e21e8c3ee755e6a2c00101010001010103000000000001010000010101000101010300000000000000010600");
3559 }
3560
3561 #[test]
3562 fn test_invalid_query_field_too_long() {
3563 let (drive, contract) = setup_family_contract();
3564
3565 let platform_version = PlatformVersion::latest();
3566
3567 let document_type = contract
3568 .document_type_for_name("person")
3569 .expect("expected to get a document type");
3570
3571 let too_long_string = "t".repeat(256);
3572 let query_value = json!({
3573 "where": [
3574 ["firstName", "<", too_long_string],
3575 ],
3576 "limit": 100,
3577 "orderBy": [
3578 ["firstName", "asc"],
3579 ["lastName", "asc"],
3580 ],
3581 });
3582
3583 let where_cbor = cbor_serializer::serializable_value_to_cbor(&query_value, None)
3584 .expect("expected to serialize to cbor");
3585 let query = DriveDocumentQuery::from_cbor(
3586 where_cbor.as_slice(),
3587 &contract,
3588 document_type,
3589 &DriveConfig::default(),
3590 PlatformVersion::latest(),
3591 )
3592 .expect("fields of queries length must be under 256 bytes long");
3593 query
3594 .execute_raw_results_no_proof(&drive, None, None, platform_version)
3595 .expect_err("fields of queries length must be under 256 bytes long");
3596 }
3597
3598 #[test]
3654 fn test_valid_query_timestamp_field_with_null_value() {
3655 let (drive, contract) = setup_family_birthday_contract();
3656
3657 let platform_version = PlatformVersion::latest();
3658
3659 let document_type = contract
3660 .document_type_for_name("person")
3661 .expect("expected to get a document type");
3662
3663 let query_value = json!({
3664 "where": [
3665 ["birthday", ">=", Null],
3666 ],
3667 "limit": 100,
3668 "orderBy": [
3669 ["birthday", "asc"],
3670 ],
3671 });
3672
3673 let where_cbor = cbor_serializer::serializable_value_to_cbor(&query_value, None)
3674 .expect("expected to serialize to cbor");
3675 let query = DriveDocumentQuery::from_cbor(
3676 where_cbor.as_slice(),
3677 &contract,
3678 document_type,
3679 &DriveConfig::default(),
3680 PlatformVersion::latest(),
3681 )
3682 .expect("The query itself should be valid for a null type");
3683 query
3684 .execute_raw_results_no_proof(&drive, None, None, platform_version)
3685 .expect("a Null value doesn't make sense for a float");
3686 }
3687
3688 #[test]
3689 fn test_invalid_query_in_with_empty_array() {
3690 let (drive, contract) = setup_family_contract();
3691
3692 let platform_version = PlatformVersion::latest();
3693
3694 let document_type = contract
3695 .document_type_for_name("person")
3696 .expect("expected to get a document type");
3697
3698 let query_value = json!({
3699 "where": [
3700 ["firstName", "in", []],
3701 ],
3702 "limit": 100,
3703 "orderBy": [
3704 ["firstName", "asc"],
3705 ],
3706 });
3707
3708 let where_cbor = cbor_serializer::serializable_value_to_cbor(&query_value, None)
3709 .expect("expected to serialize to cbor");
3710 let query = DriveDocumentQuery::from_cbor(
3711 where_cbor.as_slice(),
3712 &contract,
3713 document_type,
3714 &DriveConfig::default(),
3715 PlatformVersion::latest(),
3716 )
3717 .expect("query should be valid for empty array");
3718
3719 query
3720 .execute_raw_results_no_proof(&drive, None, None, platform_version)
3721 .expect_err("query should not be able to execute for empty array");
3722 }
3723
3724 #[test]
3725 fn test_invalid_query_in_too_many_elements() {
3726 let (drive, contract) = setup_family_contract();
3727
3728 let platform_version = PlatformVersion::latest();
3729
3730 let document_type = contract
3731 .document_type_for_name("person")
3732 .expect("expected to get a document type");
3733
3734 let mut array: Vec<String> = Vec::with_capacity(101);
3735 for _ in 0..array.capacity() {
3736 array.push(String::from("a"));
3737 }
3738 let query_value = json!({
3739 "where": [
3740 ["firstName", "in", array],
3741 ],
3742 "limit": 100,
3743 "orderBy": [
3744 ["firstName", "asc"],
3745 ],
3746 });
3747
3748 let where_cbor = cbor_serializer::serializable_value_to_cbor(&query_value, None)
3749 .expect("expected to serialize to cbor");
3750 let query = DriveDocumentQuery::from_cbor(
3751 where_cbor.as_slice(),
3752 &contract,
3753 document_type,
3754 &DriveConfig::default(),
3755 PlatformVersion::latest(),
3756 )
3757 .expect("query is valid for too many elements");
3758
3759 query
3760 .execute_raw_results_no_proof(&drive, None, None, platform_version)
3761 .expect_err("query should not be able to execute with too many elements");
3762 }
3763
3764 #[test]
3765 fn test_invalid_query_in_unique_elements() {
3766 let (drive, contract) = setup_family_contract();
3767
3768 let platform_version = PlatformVersion::latest();
3769
3770 let document_type = contract
3771 .document_type_for_name("person")
3772 .expect("expected to get a document type");
3773
3774 let query_value = json!({
3775 "where": [
3776 ["firstName", "in", ["a", "a"]],
3777 ],
3778 "limit": 100,
3779 "orderBy": [
3780 ["firstName", "asc"],
3781 ],
3782 });
3783
3784 let where_cbor = cbor_serializer::serializable_value_to_cbor(&query_value, None)
3785 .expect("expected to serialize to cbor");
3786
3787 let query = DriveDocumentQuery::from_cbor(
3791 where_cbor.as_slice(),
3792 &contract,
3793 document_type,
3794 &DriveConfig::default(),
3795 PlatformVersion::latest(),
3796 )
3797 .expect("the query should be created");
3798
3799 query
3800 .execute_raw_results_no_proof(&drive, None, None, platform_version)
3801 .expect_err("there should be no duplicates values for In query");
3802 }
3803
3804 #[test]
3805 fn test_invalid_query_starts_with_empty_string() {
3806 let query_value = json!({
3807 "where": [
3808 ["firstName", "startsWith", ""],
3809 ],
3810 "limit": 100,
3811 "orderBy": [
3812 ["firstName", "asc"],
3813 ],
3814 });
3815
3816 let contract = get_data_contract_fixture(None, 0, 1).data_contract_owned();
3817 let document_type = contract
3818 .document_type_for_name("niceDocument")
3819 .expect("expected to get nice document");
3820
3821 let where_cbor = cbor_serializer::serializable_value_to_cbor(&query_value, None)
3822 .expect("expected to serialize to cbor");
3823 DriveDocumentQuery::from_cbor(
3824 where_cbor.as_slice(),
3825 &contract,
3826 document_type,
3827 &DriveConfig::default(),
3828 PlatformVersion::latest(),
3829 )
3830 .expect_err("starts with can not start with an empty string");
3831 }
3832
3833 #[test]
3834 fn test_invalid_query_limit_too_high() {
3835 let query_value = json!({
3836 "where": [
3837 ["firstName", "startsWith", "a"],
3838 ],
3839 "limit": 101,
3840 "orderBy": [
3841 ["firstName", "asc"],
3842 ],
3843 });
3844
3845 let contract = get_data_contract_fixture(None, 0, 1).data_contract_owned();
3846 let document_type = contract
3847 .document_type_for_name("niceDocument")
3848 .expect("expected to get nice document");
3849
3850 let where_cbor = cbor_serializer::serializable_value_to_cbor(&query_value, None)
3851 .expect("expected to serialize to cbor");
3852 DriveDocumentQuery::from_cbor(
3853 where_cbor.as_slice(),
3854 &contract,
3855 document_type,
3856 &DriveConfig::default(),
3857 PlatformVersion::latest(),
3858 )
3859 .expect_err("starts with can not start with an empty string");
3860 }
3861
3862 #[test]
3863 fn test_invalid_query_limit_too_low() {
3864 let query_value = json!({
3865 "where": [
3866 ["firstName", "startsWith", "a"],
3867 ],
3868 "limit": -1,
3869 "orderBy": [
3870 ["firstName", "asc"],
3871 ],
3872 });
3873
3874 let contract = get_data_contract_fixture(None, 0, 1).data_contract_owned();
3875 let document_type = contract
3876 .document_type_for_name("niceDocument")
3877 .expect("expected to get nice document");
3878
3879 let where_cbor = cbor_serializer::serializable_value_to_cbor(&query_value, None)
3880 .expect("expected to serialize to cbor");
3881 DriveDocumentQuery::from_cbor(
3882 where_cbor.as_slice(),
3883 &contract,
3884 document_type,
3885 &DriveConfig::default(),
3886 PlatformVersion::latest(),
3887 )
3888 .expect_err("starts with can not start with an empty string");
3889 }
3890
3891 #[test]
3892 fn test_invalid_query_limit_zero() {
3893 let query_value = json!({
3894 "where": [
3895 ["firstName", "startsWith", "a"],
3896 ],
3897 "limit": 0,
3898 "orderBy": [
3899 ["firstName", "asc"],
3900 ],
3901 });
3902
3903 let contract = get_data_contract_fixture(None, 0, 1).data_contract_owned();
3904 let document_type = contract
3905 .document_type_for_name("niceDocument")
3906 .expect("expected to get nice document");
3907
3908 let where_cbor = cbor_serializer::serializable_value_to_cbor(&query_value, None)
3909 .expect("expected to serialize to cbor");
3910 DriveDocumentQuery::from_cbor(
3911 where_cbor.as_slice(),
3912 &contract,
3913 document_type,
3914 &DriveConfig::default(),
3915 PlatformVersion::latest(),
3916 )
3917 .expect_err("starts with can not start with an empty string");
3918 }
3919
3920 #[test]
3921 fn resolved_time_range_shape_guard_accepts_only_the_single_resolution_equality() {
3922 use crate::query::{validate_resolved_time_range_clause_shapes, ResolvedTimeRange};
3923 use dpp::data_contract::document_type::TimeRangeTransform;
3924
3925 let resolved = vec![ResolvedTimeRange {
3926 transform: TimeRangeTransform {
3927 source: "$createdAt".to_string(),
3928 range_seconds: 21_600,
3929 step_seconds: 7_200,
3930 phase_seconds: 0,
3931 ttl_seconds: None,
3932 },
3933 }];
3934 let equality = WhereClause {
3935 field: "$createdAt".to_string(),
3936 operator: WhereOperator::Equal,
3937 value: Value::U64(21_600_000),
3938 };
3939 let other = WhereClause {
3940 field: "hashtag".to_string(),
3941 operator: WhereOperator::Equal,
3942 value: Value::Text("ibiza".to_string()),
3943 };
3944
3945 validate_resolved_time_range_clause_shapes(&[equality.clone(), other.clone()], &resolved)
3946 .expect("one equality on the resolved field is the resolution shape");
3947
3948 let in_clause = WhereClause {
3951 field: "$createdAt".to_string(),
3952 operator: WhereOperator::In,
3953 value: Value::Array(vec![Value::U64(0), Value::U64(7_200_000)]),
3954 };
3955 validate_resolved_time_range_clause_shapes(&[in_clause, other.clone()], &resolved)
3956 .expect_err("an In clause on a resolved field must be rejected");
3957
3958 let range_clause = WhereClause {
3959 field: "$createdAt".to_string(),
3960 operator: WhereOperator::GreaterThan,
3961 value: Value::U64(0),
3962 };
3963 validate_resolved_time_range_clause_shapes(&[equality.clone(), range_clause], &resolved)
3964 .expect_err("a range clause riding along on a resolved field must be rejected");
3965
3966 validate_resolved_time_range_clause_shapes(&[other], &resolved)
3967 .expect_err("a resolved field with no equality at all must be rejected");
3968 }
3969
3970 #[test]
3976 fn by_start_rejects_windows_past_the_ttl_horizon() {
3977 use crate::query::{resolve_time_range_bucket_clause, TimeRangeSelector};
3978 use dpp::data_contract::DataContractFactory;
3979 use dpp::platform_value::platform_value;
3980 use dpp::prelude::Identifier;
3981
3982 let factory =
3983 DataContractFactory::new(PlatformVersion::latest().protocol_version).expect("factory");
3984 let hour_ms: u64 = 3_600_000;
3985 let build = |seed: u8, with_ttl: bool| {
3986 let mut time_range = vec![
3987 (
3988 Value::Text("on".to_string()),
3989 Value::Text("$createdAt".to_string()),
3990 ),
3991 (Value::Text("range".to_string()), Value::U64(7_200)),
3992 (Value::Text("step".to_string()), Value::U64(7_200)),
3993 ];
3994 if with_ttl {
3995 time_range.push((Value::Text("ttl".to_string()), Value::U64(14_400)));
3996 }
3997 let index_map = vec![
3998 (
3999 Value::Text("name".to_string()),
4000 Value::Text("trending".to_string()),
4001 ),
4002 (
4003 Value::Text("properties".to_string()),
4004 Value::Array(vec![
4005 platform_value!({"$createdAt": "asc"}),
4006 platform_value!({"hashtag": "asc"}),
4007 ]),
4008 ),
4009 (Value::Text("timeRange".to_string()), Value::Map(time_range)),
4010 (
4011 Value::Text("countable".to_string()),
4012 Value::Text("countable".to_string()),
4013 ),
4014 ];
4015 let document_schema = platform_value!({
4016 "type": "object",
4017 "properties": {
4018 "hashtag": {"type": "string", "maxLength": 61, "position": 0},
4019 },
4020 "required": ["hashtag", "$createdAt"],
4021 "indices": Value::Array(vec![Value::Map(index_map)]),
4022 "additionalProperties": false,
4023 });
4024 factory
4025 .create_with_value_config(
4026 Identifier::from([seed; 32]),
4027 0,
4028 platform_value!({ "post": document_schema }),
4029 None,
4030 None,
4031 )
4032 .expect("contract registers")
4033 .data_contract_owned()
4034 };
4035
4036 let ttl_contract = build(101, true);
4037 let standing_contract = build(102, false);
4038 let expired_start = 5_000 * hour_ms;
4039 let block_time = expired_start + 6 * hour_ms;
4040
4041 let resolve = |contract: &DataContract, start_ms: u64| {
4042 resolve_time_range_bucket_clause(
4043 "$createdAt",
4044 TimeRangeSelector::ByStart { start_ms },
4045 None,
4046 contract
4047 .document_type_for_name("post")
4048 .expect("document type"),
4049 block_time,
4050 )
4051 };
4052
4053 let error = resolve(&ttl_contract, expired_start)
4054 .expect_err("a window past the ttl horizon must be rejected, not served");
4055 assert!(
4056 error.to_string().contains("ttl horizon"),
4057 "the rejection names the horizon: {error}"
4058 );
4059 resolve(&ttl_contract, expired_start + 2 * hour_ms).expect(
4060 "a window starting exactly at the horizon is not expired — same \
4061 strictly-below boundary the drain uses",
4062 );
4063 resolve(&ttl_contract, expired_start + 4 * hour_ms)
4064 .expect("a live window resolves normally");
4065 resolve_time_range_bucket_clause(
4066 "$createdAt",
4067 TimeRangeSelector::Newest,
4068 None,
4069 ttl_contract
4070 .document_type_for_name("post")
4071 .expect("document type"),
4072 block_time,
4073 )
4074 .expect("relative selectors never address expired windows and stay unaffected");
4075 resolve(&standing_contract, expired_start)
4076 .expect("without a ttl, arbitrarily old windows stay queryable");
4077 }
4078
4079 #[test]
4080 fn test_withdrawal_query_with_missing_transaction_index() {
4081 let (_, contract) = setup_withdrawal_contract();
4083 let platform_version = PlatformVersion::latest();
4084
4085 let document_type_name = "withdrawal";
4086 let document_type = contract
4087 .document_type_for_name(document_type_name)
4088 .expect("expected to get document type");
4089
4090 let drive_document_query = DriveDocumentQuery {
4092 contract: &contract,
4093 document_type,
4094 internal_clauses: InternalClauses {
4095 primary_key_in_clause: None,
4096 primary_key_equal_clause: None,
4097 in_clauses: vec![WhereClause {
4098 field: "status".to_string(),
4099 operator: WhereOperator::In,
4100 value: Value::Array(vec![
4101 Value::U64(0),
4102 Value::U64(1),
4103 Value::U64(2),
4104 Value::U64(3),
4105 Value::U64(4),
4106 ]),
4107 }],
4108 range_clause: None,
4109 equal_clauses: BTreeMap::default(),
4110 },
4111 offset: None,
4112 limit: Some(3),
4113 order_by: IndexMap::from([
4114 (
4115 "status".to_string(),
4116 OrderClause {
4117 field: "status".to_string(),
4118 ascending: true,
4119 },
4120 ),
4121 (
4122 "transactionIndex".to_string(),
4123 OrderClause {
4124 field: "transactionIndex".to_string(),
4125 ascending: true,
4126 },
4127 ),
4128 ]),
4129 start_at: Some([3u8; 32]),
4130 start_at_included: false,
4131 block_time_ms: None,
4132 resolved_time_ranges: vec![],
4133 sub_queries: vec![],
4134 };
4135
4136 let mut properties = BTreeMap::new();
4138 properties.insert("status".to_string(), Value::U64(0));
4139 let starts_at_document = DocumentV0 {
4142 contract_version: None,
4143 id: Identifier::from([3u8; 32]), owner_id: Identifier::random(),
4145 properties,
4146 revision: None,
4147 created_at: None,
4148 updated_at: None,
4149 transferred_at: None,
4150 created_at_block_height: None,
4151 updated_at_block_height: None,
4152 transferred_at_block_height: None,
4153 created_at_core_block_height: None,
4154 updated_at_core_block_height: None,
4155 transferred_at_core_block_height: None,
4156 creator_id: None,
4157 }
4158 .into();
4159
4160 let result = drive_document_query
4162 .construct_path_query(Some(starts_at_document), platform_version)
4163 .expect("expected to construct a path query");
4164
4165 assert_eq!(
4166 result
4167 .clone()
4168 .query
4169 .query
4170 .default_subquery_branch
4171 .subquery
4172 .expect("expected subquery")
4173 .items,
4174 Query::new_range_full().items
4175 );
4176 }
4177
4178 mod multiple_in_clause_lowering {
4185 use super::*;
4186 use crate::error::query::QuerySyntaxError;
4187 use crate::error::Error;
4188
4189 fn family_contract() -> DataContract {
4190 json_document_to_contract(
4191 "tests/supporting_files/contract/family/family-contract.json",
4192 false,
4193 PlatformVersion::latest(),
4194 )
4195 .expect("expected to load family contract")
4196 }
4197
4198 fn text_array(values: &[&str]) -> Value {
4199 Value::Array(
4200 values
4201 .iter()
4202 .map(|value| Value::Text(value.to_string()))
4203 .collect(),
4204 )
4205 }
4206
4207 fn in_clause(field: &str, values: &[&str]) -> WhereClause {
4208 WhereClause {
4209 field: field.to_string(),
4210 operator: WhereOperator::In,
4211 value: text_array(values),
4212 }
4213 }
4214
4215 fn ascending_order_by(fields: &[&str]) -> IndexMap<String, OrderClause> {
4216 fields
4217 .iter()
4218 .map(|field| {
4219 (
4220 field.to_string(),
4221 OrderClause {
4222 field: field.to_string(),
4223 ascending: true,
4224 },
4225 )
4226 })
4227 .collect()
4228 }
4229
4230 fn person_query<'a>(
4231 contract: &'a DataContract,
4232 where_clauses: Vec<WhereClause>,
4233 order_by_fields: &[&str],
4234 ) -> DriveDocumentQuery<'a> {
4235 let internal_clauses =
4236 InternalClauses::extract_from_clauses(where_clauses, PlatformVersion::latest())
4237 .expect("clauses should group structurally");
4238 DriveDocumentQuery {
4239 contract,
4240 document_type: contract
4241 .document_type_for_name("person")
4242 .expect("person document type should exist"),
4243 internal_clauses,
4244 offset: None,
4245 limit: Some(100),
4246 order_by: ascending_order_by(order_by_fields),
4247 start_at: None,
4248 start_at_included: false,
4249 block_time_ms: None,
4250 resolved_time_ranges: vec![],
4251 sub_queries: vec![],
4252 }
4253 }
4254
4255 #[test]
4256 fn two_in_clauses_lower_to_nested_key_sets() {
4257 let contract = family_contract();
4258 let platform_version = PlatformVersion::latest();
4259 let query = person_query(
4260 &contract,
4261 vec![
4262 in_clause("firstName", &["Adey", "Briney"]),
4263 in_clause("lastName", &["Kriskov", "Randolf"]),
4264 ],
4265 &["firstName", "lastName"],
4266 );
4267
4268 let path_query = query
4269 .construct_path_query(None, platform_version)
4270 .expect("two in clauses should lower at protocol version 14");
4271
4272 assert_eq!(
4275 path_query.path.last().expect("path should not be empty"),
4276 &b"firstName".to_vec()
4277 );
4278
4279 let outer = &path_query.query.query;
4281 assert_eq!(outer.items.len(), 2);
4282 assert!(outer.left_to_right);
4283
4284 assert_eq!(
4287 outer.default_subquery_branch.subquery_path,
4288 Some(vec![b"lastName".to_vec()])
4289 );
4290 let inner = outer
4291 .default_subquery_branch
4292 .subquery
4293 .as_deref()
4294 .expect("expected a lastName subquery");
4295 assert_eq!(inner.items.len(), 2);
4296
4297 assert_eq!(
4299 inner.default_subquery_branch.subquery_path,
4300 Some(vec![vec![0]])
4301 );
4302 }
4303
4304 #[test]
4305 #[cfg(feature = "cbor_query")]
4306 fn two_in_clauses_survive_cbor_round_trip() {
4307 let contract = family_contract();
4308 let mut query = person_query(
4309 &contract,
4310 vec![
4311 in_clause("firstName", &["Adey", "Briney"]),
4312 in_clause("lastName", &["Kriskov", "Randolf"]),
4313 ],
4314 &["firstName", "lastName"],
4315 );
4316 query.start_at_included = true;
4319
4320 let cbor = query.to_cbor().expect("should serialize cbor");
4321 let deserialized = DriveDocumentQuery::from_cbor(
4322 &cbor,
4323 &contract,
4324 contract
4325 .document_type_for_name("person")
4326 .expect("person document type should exist"),
4327 &DriveConfig::default(),
4328 PlatformVersion::latest(),
4329 )
4330 .expect("should deserialize cbor");
4331
4332 assert_eq!(query, deserialized);
4333 assert_eq!(
4334 deserialized
4335 .internal_clauses
4336 .in_clauses
4337 .iter()
4338 .map(|in_clause| in_clause.field.as_str())
4339 .collect::<Vec<_>>(),
4340 vec!["firstName", "lastName"],
4341 "both in clauses must survive the round trip in order"
4342 );
4343 }
4344
4345 #[test]
4346 fn descending_order_by_on_left_over_property_is_honored() {
4347 let contract = family_contract();
4348 let platform_version = PlatformVersion::latest();
4349 let mut query = person_query(
4352 &contract,
4353 vec![
4354 in_clause("firstName", &["Adey", "Briney"]),
4355 in_clause("middleName", &["Ivanna", "Evangeline"]),
4356 ],
4357 &["firstName", "middleName"],
4358 );
4359 query.order_by.insert(
4360 "lastName".to_string(),
4361 OrderClause {
4362 field: "lastName".to_string(),
4363 ascending: false,
4364 },
4365 );
4366
4367 let path_query = query
4368 .construct_path_query(None, platform_version)
4369 .expect("two in clauses with a left-over order should lower");
4370
4371 let outer = &path_query.query.query;
4372 let middle = outer
4373 .default_subquery_branch
4374 .subquery
4375 .as_deref()
4376 .expect("expected a middleName subquery");
4377 assert_eq!(
4378 middle.default_subquery_branch.subquery_path,
4379 Some(vec![b"lastName".to_vec()])
4380 );
4381 let left_over_level = middle
4382 .default_subquery_branch
4383 .subquery
4384 .as_deref()
4385 .expect("expected a lastName subquery");
4386 assert!(
4387 !left_over_level.left_to_right,
4388 "left-over lastName level must honor the descending order by"
4389 );
4390
4391 query.order_by.shift_remove("lastName");
4394 let path_query = query
4395 .construct_path_query(None, platform_version)
4396 .expect("two in clauses should lower");
4397 let left_over_level = path_query
4398 .query
4399 .query
4400 .default_subquery_branch
4401 .subquery
4402 .as_deref()
4403 .expect("expected a middleName subquery")
4404 .default_subquery_branch
4405 .subquery
4406 .as_deref()
4407 .expect("expected a lastName subquery");
4408 assert!(left_over_level.left_to_right);
4409 }
4410
4411 #[test]
4412 fn two_in_clauses_rejected_at_protocol_version_13() {
4413 let contract = family_contract();
4414 let platform_version_13 =
4415 PlatformVersion::get(13).expect("protocol version 13 should exist");
4416 let query = person_query(
4417 &contract,
4418 vec![
4419 in_clause("firstName", &["Adey", "Briney"]),
4420 in_clause("lastName", &["Kriskov", "Randolf"]),
4421 ],
4422 &["firstName", "lastName"],
4423 );
4424
4425 let error = query
4426 .construct_path_query(None, platform_version_13)
4427 .expect_err("multiple in clauses must be rejected before protocol version 14");
4428 assert!(
4429 matches!(error, Error::Query(QuerySyntaxError::MultipleInClauses(_))),
4430 "expected MultipleInClauses, got {error:?}"
4431 );
4432
4433 query
4434 .construct_path_query(None, PlatformVersion::latest())
4435 .expect("the same query should lower at protocol version 14");
4436 }
4437
4438 #[test]
4439 fn equality_prefix_two_in_clauses_and_trailing_range_lowering() {
4440 let contract = family_contract();
4441 let platform_version = PlatformVersion::latest();
4442 let mut query = person_query(
4443 &contract,
4444 vec![
4445 WhereClause {
4446 field: "age".to_string(),
4447 operator: WhereOperator::Equal,
4448 value: Value::U8(30),
4449 },
4450 in_clause("firstName", &["Adey", "Briney"]),
4451 in_clause("middleName", &["Ivanna", "Evangeline"]),
4452 WhereClause {
4453 field: "lastName".to_string(),
4454 operator: WhereOperator::GreaterThan,
4455 value: Value::Text("M".to_string()),
4456 },
4457 ],
4458 &["firstName", "middleName", "lastName"],
4459 );
4460 query.limit = Some(50);
4461
4462 let path_query = query
4466 .construct_path_query(None, platform_version)
4467 .expect("equality + in + in + range should lower");
4468
4469 let path_len = path_query.path.len();
4470 assert_eq!(path_query.path[path_len - 3], b"age".to_vec());
4471 assert_eq!(
4472 path_query.path.last().expect("path should not be empty"),
4473 &b"firstName".to_vec()
4474 );
4475
4476 let outer = &path_query.query.query;
4477 assert_eq!(outer.items.len(), 2);
4478 assert_eq!(
4479 outer.default_subquery_branch.subquery_path,
4480 Some(vec![b"middleName".to_vec()])
4481 );
4482 let middle = outer
4483 .default_subquery_branch
4484 .subquery
4485 .as_deref()
4486 .expect("expected a middleName subquery");
4487 assert_eq!(middle.items.len(), 2);
4488 assert_eq!(
4489 middle.default_subquery_branch.subquery_path,
4490 Some(vec![b"lastName".to_vec()])
4491 );
4492 let range_level = middle
4493 .default_subquery_branch
4494 .subquery
4495 .as_deref()
4496 .expect("expected a lastName subquery");
4497 assert_eq!(range_level.items.len(), 1);
4499 assert_eq!(
4500 range_level.default_subquery_branch.subquery_path,
4501 Some(vec![vec![0]])
4502 );
4503 }
4504
4505 #[test]
4506 fn cross_product_above_cap_is_rejected() {
4507 let contract = family_contract();
4508 let first_names: Vec<String> = (0..20).map(|i| format!("First{i:02}")).collect();
4509 let last_names: Vec<String> = (0..6).map(|i| format!("Last{i}")).collect();
4510 let query = person_query(
4511 &contract,
4512 vec![
4513 WhereClause {
4514 field: "firstName".to_string(),
4515 operator: WhereOperator::In,
4516 value: Value::Array(first_names.iter().cloned().map(Value::Text).collect()),
4517 },
4518 WhereClause {
4519 field: "lastName".to_string(),
4520 operator: WhereOperator::In,
4521 value: Value::Array(last_names.iter().cloned().map(Value::Text).collect()),
4522 },
4523 ],
4524 &["firstName", "lastName"],
4525 );
4526
4527 let error = query
4528 .construct_path_query(None, PlatformVersion::latest())
4529 .expect_err("a 120-branch cross product must be rejected");
4530 assert!(
4531 matches!(error, Error::Query(QuerySyntaxError::InvalidInClause(_))),
4532 "expected InvalidInClause, got {error:?}"
4533 );
4534 }
4535
4536 #[test]
4537 fn non_consecutive_in_fields_are_rejected() {
4538 let contract = family_contract();
4539 let query = person_query(
4543 &contract,
4544 vec![
4545 in_clause("middleName", &["Ivanna", "Evangeline"]),
4546 in_clause("lastName", &["Kriskov", "Randolf"]),
4547 ],
4548 &["middleName", "lastName"],
4549 );
4550
4551 let error = query
4552 .construct_path_query(None, PlatformVersion::latest())
4553 .expect_err("non-consecutive in clauses must be rejected");
4554 assert!(
4555 matches!(
4556 error,
4557 Error::Query(QuerySyntaxError::WhereClauseOnNonIndexedProperty(_))
4558 ),
4559 "expected WhereClauseOnNonIndexedProperty, got {error:?}"
4560 );
4561 }
4562
4563 #[test]
4564 fn cursor_pagination_is_rejected() {
4565 let contract = family_contract();
4566 let mut query = person_query(
4567 &contract,
4568 vec![
4569 in_clause("firstName", &["Adey", "Briney"]),
4570 in_clause("lastName", &["Kriskov", "Randolf"]),
4571 ],
4572 &["firstName", "lastName"],
4573 );
4574 query.start_at = Some([5u8; 32]);
4575 query.start_at_included = false;
4576
4577 let error = query
4578 .construct_path_query(None, PlatformVersion::latest())
4579 .expect_err("cursor pagination with multiple in clauses must be rejected");
4580 assert!(
4581 matches!(error, Error::Query(QuerySyntaxError::Unsupported(_))),
4582 "expected Unsupported, got {error:?}"
4583 );
4584 }
4585
4586 #[test]
4587 fn missing_order_by_on_an_in_field_is_rejected() {
4588 let contract = family_contract();
4589 let query = person_query(
4590 &contract,
4591 vec![
4592 in_clause("firstName", &["Adey", "Briney"]),
4593 in_clause("lastName", &["Kriskov", "Randolf"]),
4594 ],
4595 &["firstName"],
4596 );
4597
4598 let error = query
4599 .construct_path_query(None, PlatformVersion::latest())
4600 .expect_err("missing order by on an in field must be rejected");
4601 assert!(
4606 matches!(
4607 error,
4608 Error::Query(QuerySyntaxError::WhereClauseOnNonIndexedProperty(_))
4609 ),
4610 "expected WhereClauseOnNonIndexedProperty, got {error:?}"
4611 );
4612 }
4613 }
4614}