1use crate::error::drive::DriveError;
5use crate::error::query::QuerySyntaxError;
6use crate::error::Error;
7use crate::query::where_clause_grouping::group_where_clauses;
8use crate::query::{QuerySyntaxSimpleValidationResult, QuerySyntaxValidationResult};
9#[cfg(any(feature = "server", feature = "verify"))]
10use dpp::data_contract::document_type::accessors::DocumentTypeV0Getters;
11use dpp::data_contract::document_type::methods::DocumentTypeV0Methods;
12#[cfg(any(feature = "server", feature = "verify"))]
13use dpp::data_contract::document_type::MAX_INDEX_SIZE;
14use dpp::data_contract::document_type::{
15 typed_array_property, DocumentPropertyType, DocumentType, DocumentTypeRef,
16};
17use dpp::document::document_methods::DocumentMethodsV0;
18use dpp::document::Document;
19use dpp::platform_value::Value;
20use dpp::version::PlatformVersion;
21use grovedb::Query;
22use sqlparser::ast;
23use std::borrow::Cow;
24use std::cmp::Ordering;
25use std::collections::BTreeMap;
26use std::fmt::Display;
27use WhereOperator::{
28 Between, BetweenExcludeBounds, BetweenExcludeLeft, BetweenExcludeRight, Contains, ContainsAny,
29 Equal, GreaterThan, GreaterThanOrEquals, In, LessThan, LessThanOrEquals, StartsWith,
30};
31
32fn sql_value_to_platform_value(sql_value: ast::Value) -> Option<Value> {
34 match sql_value {
35 ast::Value::Boolean(bool) => Some(Value::Bool(bool)),
36 ast::Value::Number(num, _) => {
37 let number_as_string = num as String;
38 if number_as_string.contains('.') {
39 let num_as_float = number_as_string.parse::<f64>().ok();
41 num_as_float.map(Value::Float)
42 } else {
43 let num_as_int = number_as_string.parse::<i64>().ok();
45 num_as_int.map(Value::I64)
46 }
47 }
48 ast::Value::DoubleQuotedString(s) => Some(Value::Text(s)),
49 ast::Value::SingleQuotedString(s) => Some(Value::Text(s)),
50 ast::Value::HexStringLiteral(s) => Some(Value::Text(s)),
51 ast::Value::NationalStringLiteral(s) => Some(Value::Text(s)),
52 _ => None,
53 }
54}
55
56#[derive(Copy, Clone, Debug, PartialEq, Eq)]
58#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
59pub enum WhereOperator {
60 Equal,
62 GreaterThan,
64 GreaterThanOrEquals,
66 LessThan,
68 LessThanOrEquals,
70 Between,
72 BetweenExcludeBounds,
74 BetweenExcludeLeft,
76 BetweenExcludeRight,
78 In,
80 StartsWith,
82 Contains,
86 ContainsAny,
93}
94
95impl WhereOperator {
96 pub fn allows_flip(&self) -> bool {
98 match self {
99 Equal => true,
100 GreaterThan => true,
101 GreaterThanOrEquals => true,
102 LessThan => true,
103 LessThanOrEquals => true,
104 Between => false,
105 BetweenExcludeBounds => false,
106 BetweenExcludeLeft => false,
107 BetweenExcludeRight => false,
108 In => false,
109 StartsWith => false,
110 Contains => false,
111 ContainsAny => false,
112 }
113 }
114
115 pub fn flip(&self) -> Result<WhereOperator, Error> {
117 match self {
118 Equal => Ok(Equal),
119 GreaterThan => Ok(LessThan),
120 GreaterThanOrEquals => Ok(LessThanOrEquals),
121 LessThan => Ok(GreaterThan),
122 LessThanOrEquals => Ok(GreaterThanOrEquals),
123 Between => Err(Error::Query(QuerySyntaxError::InvalidWhereClauseOrder(
124 "Between clause order invalid",
125 ))),
126 BetweenExcludeBounds => Err(Error::Query(QuerySyntaxError::InvalidWhereClauseOrder(
127 "Between clause order invalid",
128 ))),
129 BetweenExcludeLeft => Err(Error::Query(QuerySyntaxError::InvalidWhereClauseOrder(
130 "Between clause order invalid",
131 ))),
132 BetweenExcludeRight => Err(Error::Query(QuerySyntaxError::InvalidWhereClauseOrder(
133 "Between clause order invalid",
134 ))),
135 In => Err(Error::Query(QuerySyntaxError::InvalidWhereClauseOrder(
136 "In clause order invalid",
137 ))),
138 StartsWith => Err(Error::Query(QuerySyntaxError::InvalidWhereClauseOrder(
139 "Startswith clause order invalid",
140 ))),
141 Contains => Err(Error::Query(QuerySyntaxError::InvalidWhereClauseOrder(
142 "Contains clause order invalid",
143 ))),
144 ContainsAny => Err(Error::Query(QuerySyntaxError::InvalidWhereClauseOrder(
145 "ContainsAny clause order invalid",
146 ))),
147 }
148 }
149}
150
151impl WhereOperator {
152 pub const fn is_range(self) -> bool {
154 match self {
155 Equal | Contains => false,
156 GreaterThan | GreaterThanOrEquals | LessThan | LessThanOrEquals | Between
157 | BetweenExcludeBounds | BetweenExcludeLeft | BetweenExcludeRight | In | StartsWith
158 | ContainsAny => true,
159 }
160 }
161
162 pub const fn as_key_operator(self) -> Self {
166 match self {
167 Contains => Equal,
168 ContainsAny => In,
169 other => other,
170 }
171 }
172
173 pub const fn reads_array_elements(self) -> bool {
175 matches!(self, Contains | ContainsAny)
176 }
177
178 pub(crate) fn from_string(string: &str) -> Option<Self> {
180 match string {
181 "=" | "==" => Some(Equal),
182 ">" => Some(GreaterThan),
183 ">=" => Some(GreaterThanOrEquals),
184 "<" => Some(LessThan),
185 "<=" => Some(LessThanOrEquals),
186 "Between" | "between" => Some(Between),
187 "BetweenExcludeBounds"
188 | "betweenExcludeBounds"
189 | "betweenexcludebounds"
190 | "between_exclude_bounds" => Some(BetweenExcludeBounds),
191 "BetweenExcludeLeft"
192 | "betweenExcludeLeft"
193 | "betweenexcludeleft"
194 | "between_exclude_left" => Some(BetweenExcludeLeft),
195 "BetweenExcludeRight"
196 | "betweenExcludeRight"
197 | "betweenexcluderight"
198 | "between_exclude_right" => Some(BetweenExcludeRight),
199 "In" | "in" => Some(In),
200 "StartsWith" | "startsWith" | "startswith" | "starts_with" => Some(StartsWith),
201 "Contains" | "contains" => Some(Contains),
202 "ContainsAny" | "containsAny" | "containsany" | "contains_any" => Some(ContainsAny),
203 &_ => None,
204 }
205 }
206
207 pub(crate) fn from_sql_operator(sql_operator: ast::BinaryOperator) -> Option<Self> {
209 match sql_operator {
210 ast::BinaryOperator::Eq => Some(Equal),
211 ast::BinaryOperator::Gt => Some(GreaterThan),
212 ast::BinaryOperator::GtEq => Some(GreaterThanOrEquals),
213 ast::BinaryOperator::Lt => Some(LessThan),
214 ast::BinaryOperator::LtEq => Some(LessThanOrEquals),
215 _ => None,
216 }
217 }
218
219 pub fn eval(&self, left_value: &Value, right_value: &Value) -> bool {
221 match self {
222 Equal => left_value == right_value,
223 GreaterThan => left_value > right_value,
224 GreaterThanOrEquals => left_value >= right_value,
225 LessThan => left_value < right_value,
226 LessThanOrEquals => left_value <= right_value,
227 In => match right_value {
228 Value::Array(array) => array.contains(left_value),
229 Value::Bytes(bytes) => match left_value {
230 Value::U8(b) => bytes.contains(b),
231 _ => false,
232 },
233 _ => false,
234 },
235 Between => match right_value {
236 Value::Array(bounds) if bounds.len() == 2 => {
237 match bounds[0].partial_cmp(&bounds[1]) {
238 Some(Ordering::Less) => {
239 left_value >= &bounds[0] && left_value <= &bounds[1]
240 }
241 _ => false,
242 }
243 }
244 _ => false,
245 },
246 BetweenExcludeBounds => match right_value {
247 Value::Array(bounds) if bounds.len() == 2 => {
248 match bounds[0].partial_cmp(&bounds[1]) {
249 Some(Ordering::Less) => left_value > &bounds[0] && left_value < &bounds[1],
250 _ => false,
251 }
252 }
253 _ => false,
254 },
255 BetweenExcludeLeft => match right_value {
256 Value::Array(bounds) if bounds.len() == 2 => {
257 match bounds[0].partial_cmp(&bounds[1]) {
258 Some(Ordering::Less) => left_value > &bounds[0] && left_value <= &bounds[1],
259 _ => false,
260 }
261 }
262 _ => false,
263 },
264 BetweenExcludeRight => match right_value {
265 Value::Array(bounds) if bounds.len() == 2 => {
266 match bounds[0].partial_cmp(&bounds[1]) {
267 Some(Ordering::Less) => left_value >= &bounds[0] && left_value < &bounds[1],
268 _ => false,
269 }
270 }
271 _ => false,
272 },
273 StartsWith => match (left_value, right_value) {
274 (Value::Text(text), Value::Text(prefix)) => text.starts_with(prefix.as_str()),
275 _ => false,
276 },
277 Contains => match left_value {
278 Value::Array(elements) => elements.contains(right_value),
279 _ => false,
280 },
281 ContainsAny => match left_value {
284 Value::Array(elements) => {
285 elements.iter().any(|element| In.eval(element, right_value))
286 }
287 _ => false,
288 },
289 }
290 }
291
292 #[cfg(any(feature = "server", feature = "verify"))]
294 pub fn value_shape_ok(&self, value: &Value, property_type: &DocumentPropertyType) -> bool {
295 match self {
296 Equal => true,
297 In => matches!(value, Value::Array(_) | Value::Bytes(_)),
298 StartsWith => matches!(value, Value::Text(_)),
299 Contains => !matches!(value, Value::Array(_) | Value::Null),
301 ContainsAny => matches!(value, Value::Array(_)),
302 GreaterThan | GreaterThanOrEquals | LessThan | LessThanOrEquals => {
303 match property_type {
304 DocumentPropertyType::F64 => is_numeric_value(value),
305 DocumentPropertyType::String(_) => {
306 matches!(value, Value::Text(_))
307 }
308 _ => matches!(
309 value,
310 Value::U128(_)
311 | Value::I128(_)
312 | Value::U64(_)
313 | Value::I64(_)
314 | Value::U32(_)
315 | Value::I32(_)
316 | Value::U16(_)
317 | Value::I16(_)
318 | Value::U8(_)
319 | Value::I8(_)
320 ),
321 }
322 }
323 Between | BetweenExcludeBounds | BetweenExcludeLeft | BetweenExcludeRight => {
324 if let Value::Array(arr) = value {
325 arr.len() == 2
326 && arr.iter().all(|x| match property_type {
327 DocumentPropertyType::F64 => is_numeric_value(x),
328 DocumentPropertyType::String(_) => {
329 matches!(x, Value::Text(_))
330 }
331 _ => matches!(
332 x,
333 Value::U128(_)
334 | Value::I128(_)
335 | Value::U64(_)
336 | Value::I64(_)
337 | Value::U32(_)
338 | Value::I32(_)
339 | Value::U16(_)
340 | Value::I16(_)
341 | Value::U8(_)
342 | Value::I8(_)
343 ),
344 })
345 } else {
346 false
347 }
348 }
349 }
350 }
351}
352
353impl Display for WhereOperator {
354 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
355 let s = match self {
356 Equal => "=",
357 GreaterThan => ">",
358 GreaterThanOrEquals => ">=",
359 LessThan => "<",
360 LessThanOrEquals => "<=",
361 Between => "Between",
362 BetweenExcludeBounds => "BetweenExcludeBounds",
363 BetweenExcludeLeft => "BetweenExcludeLeft",
364 BetweenExcludeRight => "BetweenExcludeRight",
365 In => "In",
366 StartsWith => "StartsWith",
367 Contains => "Contains",
368 ContainsAny => "ContainsAny",
369 };
370
371 write!(f, "{}", s)
372 }
373}
374
375impl From<WhereOperator> for Value {
376 fn from(value: WhereOperator) -> Self {
377 Self::Text(value.to_string())
378 }
379}
380
381#[derive(Clone, Debug, PartialEq)]
383#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
384pub struct WhereClause {
385 pub field: String,
387 pub operator: WhereOperator,
389 pub value: Value,
391}
392
393impl<'a> WhereClause {
394 pub fn is_identifier(&self) -> bool {
396 self.field == "$id"
397 }
398
399 pub fn in_values(&self) -> QuerySyntaxValidationResult<Cow<'_, Vec<Value>>> {
401 let in_values = match &self.value {
402 Value::Array(array) => Cow::Borrowed(array),
403 Value::Bytes(bytes) => Cow::Owned(bytes.iter().map(|int| Value::U8(*int)).collect()),
404 _ => {
405 return QuerySyntaxValidationResult::new_with_error(
406 QuerySyntaxError::InvalidInClause(
407 "when using in operator you must provide an array of values".to_string(),
408 ),
409 )
410 }
411 };
412
413 let len = in_values.len();
414 if len == 0 {
415 return QuerySyntaxValidationResult::new_with_error(QuerySyntaxError::InvalidInClause(
416 "in clause must have at least 1 value".to_string(),
417 ));
418 }
419
420 if len > 100 {
421 return QuerySyntaxValidationResult::new_with_error(QuerySyntaxError::InvalidInClause(
422 "in clause must have at most 100 values".to_string(),
423 ));
424 }
425
426 if (1..in_values.len()).any(|i| in_values[i..].contains(&in_values[i - 1])) {
428 return QuerySyntaxValidationResult::new_with_error(QuerySyntaxError::InvalidInClause(
429 "there should be no duplicates values for In query".to_string(),
430 ));
431 }
432 QuerySyntaxValidationResult::new_with_data(in_values)
433 }
434
435 pub fn less_than(&self, other: &Self, allow_eq: bool) -> Result<bool, Error> {
437 match (&self.value, &other.value) {
438 (Value::I128(x), Value::I128(y)) => {
439 if allow_eq {
440 Ok(x.le(y))
441 } else {
442 Ok(x.lt(y))
443 }
444 }
445 (Value::U128(x), Value::U128(y)) => {
446 if allow_eq {
447 Ok(x.le(y))
448 } else {
449 Ok(x.lt(y))
450 }
451 }
452 (Value::I64(x), Value::I64(y)) => {
453 if allow_eq {
454 Ok(x.le(y))
455 } else {
456 Ok(x.lt(y))
457 }
458 }
459 (Value::U64(x), Value::U64(y)) => {
460 if allow_eq {
461 Ok(x.le(y))
462 } else {
463 Ok(x.lt(y))
464 }
465 }
466 (Value::I32(x), Value::I32(y)) => {
467 if allow_eq {
468 Ok(x.le(y))
469 } else {
470 Ok(x.lt(y))
471 }
472 }
473 (Value::U32(x), Value::U32(y)) => {
474 if allow_eq {
475 Ok(x.le(y))
476 } else {
477 Ok(x.lt(y))
478 }
479 }
480 (Value::I16(x), Value::I16(y)) => {
481 if allow_eq {
482 Ok(x.le(y))
483 } else {
484 Ok(x.lt(y))
485 }
486 }
487 (Value::U16(x), Value::U16(y)) => {
488 if allow_eq {
489 Ok(x.le(y))
490 } else {
491 Ok(x.lt(y))
492 }
493 }
494 (Value::I8(x), Value::I8(y)) => {
495 if allow_eq {
496 Ok(x.le(y))
497 } else {
498 Ok(x.lt(y))
499 }
500 }
501 (Value::U8(x), Value::U8(y)) => {
502 if allow_eq {
503 Ok(x.le(y))
504 } else {
505 Ok(x.lt(y))
506 }
507 }
508 (Value::Bytes(x), Value::Bytes(y)) => {
509 if allow_eq {
510 Ok(x.le(y))
511 } else {
512 Ok(x.lt(y))
513 }
514 }
515 (Value::Float(x), Value::Float(y)) => {
516 if allow_eq {
517 Ok(x.le(y))
518 } else {
519 Ok(x.lt(y))
520 }
521 }
522 (Value::Text(x), Value::Text(y)) => {
523 if allow_eq {
524 Ok(x.le(y))
525 } else {
526 Ok(x.lt(y))
527 }
528 }
529 _ => Err(Error::Query(QuerySyntaxError::RangeClausesNotGroupable(
530 "range clauses can not be coherently grouped",
531 ))),
532 }
533 }
534
535 pub fn from_components(clause_components: &'a [Value]) -> Result<Self, Error> {
537 if clause_components.len() != 3 {
538 return Err(Error::Query(
539 QuerySyntaxError::InvalidWhereClauseComponents(
540 "where clauses should have at most 3 components",
541 ),
542 ));
543 }
544
545 let field_value = clause_components
546 .first()
547 .expect("check above enforces it exists");
548 let field_ref = field_value.as_text().ok_or(Error::Query(
549 QuerySyntaxError::InvalidWhereClauseComponents(
550 "first field of where component should be a string",
551 ),
552 ))?;
553 let field = String::from(field_ref);
554
555 let operator_value = clause_components
556 .get(1)
557 .expect("check above enforces it exists");
558 let operator_string = operator_value.as_text().ok_or(Error::Query(
559 QuerySyntaxError::InvalidWhereClauseComponents(
560 "second field of where component should be a string",
561 ),
562 ))?;
563
564 let operator = WhereOperator::from_string(operator_string).ok_or({
565 Error::Query(QuerySyntaxError::InvalidWhereClauseComponents(
566 "second field of where component should be a known operator",
567 ))
568 })?;
569
570 let value = clause_components
571 .get(2)
572 .ok_or(Error::Query(
573 QuerySyntaxError::InvalidWhereClauseComponents(
574 "third field of where component should exist",
575 ),
576 ))?
577 .clone();
578
579 Ok(WhereClause {
580 field,
581 operator,
582 value,
583 })
584 }
585
586 pub(crate) fn lower_bound_clause(
587 where_clauses: &'a [&WhereClause],
588 ) -> Result<Option<&'a Self>, Error> {
589 let lower_range_clauses: Vec<&&WhereClause> = where_clauses
590 .iter()
591 .filter(|&where_clause| {
592 matches!(where_clause.operator, GreaterThan | GreaterThanOrEquals)
593 })
594 .collect::<Vec<&&WhereClause>>();
595 match lower_range_clauses.len() {
596 0 => Ok(None),
597 1 => Ok(Some(lower_range_clauses.first().unwrap())),
598 _ => Err(Error::Query(QuerySyntaxError::MultipleRangeClauses(
599 "there can only at most one range clause with a lower bound",
600 ))),
601 }
602 }
603
604 pub(crate) fn upper_bound_clause(
605 where_clauses: &'a [&WhereClause],
606 ) -> Result<Option<&'a Self>, Error> {
607 let upper_range_clauses: Vec<&&WhereClause> = where_clauses
608 .iter()
609 .filter(|&where_clause| matches!(where_clause.operator, LessThan | LessThanOrEquals))
610 .collect::<Vec<&&WhereClause>>();
611 match upper_range_clauses.len() {
612 0 => Ok(None),
613 1 => Ok(Some(upper_range_clauses.first().unwrap())),
614 _ => Err(Error::Query(QuerySyntaxError::MultipleRangeClauses(
615 "there can only at most one range clause with a lower bound",
616 ))),
617 }
618 }
619
620 #[allow(clippy::type_complexity)]
631 pub(crate) fn group_clauses(
632 where_clauses: &'a [WhereClause],
633 platform_version: &PlatformVersion,
634 ) -> Result<(BTreeMap<String, Self>, Option<Self>, Vec<Self>), Error> {
635 group_where_clauses(where_clauses, platform_version)
636 }
637
638 fn split_value_for_between(
639 &self,
640 document_type: DocumentTypeRef,
641 platform_version: &PlatformVersion,
642 ) -> Result<(Vec<u8>, Vec<u8>), Error> {
643 let in_values = match &self.value {
644 Value::Array(array) => Some(array),
645 _ => None,
646 }
647 .ok_or({
648 Error::Query(QuerySyntaxError::InvalidBetweenClause(
649 "when using between operator you must provide a tuple array of values",
650 ))
651 })?;
652 if in_values.len() != 2 {
653 return Err(Error::Query(QuerySyntaxError::InvalidBetweenClause(
654 "when using between operator you must provide an array of exactly two values",
655 )));
656 }
657 let left_key = document_type.serialize_value_for_key(
658 self.field.as_str(),
659 in_values.first().unwrap(),
660 platform_version,
661 )?;
662 let right_key = document_type.serialize_value_for_key(
663 self.field.as_str(),
664 in_values.get(1).unwrap(),
665 platform_version,
666 )?;
667 Ok((left_key, right_key))
668 }
669
670 pub(crate) fn to_path_query(
676 &self,
677 document_type: DocumentTypeRef,
678 start_at_document: &Option<(Document, bool)>,
679 left_to_right: bool,
680 platform_version: &PlatformVersion,
681 ) -> Result<Query, Error> {
682 let starts_at_key_option = match start_at_document {
685 None => None,
686 Some((document, included)) => {
687 document
689 .get_raw_for_document_type(
690 self.field.as_str(),
691 document_type,
692 None,
693 platform_version,
694 )?
695 .map(|raw_value_option| (raw_value_option, *included))
696 }
697 };
698
699 let mut query = Query::new_with_direction(left_to_right);
700 match self.operator.as_key_operator() {
703 Equal => {
704 let key = document_type.serialize_value_for_key(
705 self.field.as_str(),
706 &self.value,
707 platform_version,
708 )?;
709 match starts_at_key_option {
710 None => {
711 query.insert_key(key);
712 }
713 Some((starts_at_key, included)) => {
714 if (left_to_right && starts_at_key < key)
715 || (!left_to_right && starts_at_key > key)
716 || (included && starts_at_key == key)
717 {
718 query.insert_key(key);
719 }
720 }
721 }
722 }
723 In => {
724 let in_values = self.in_values().into_data_with_error()??;
725
726 match starts_at_key_option {
727 None => {
728 for value in in_values.iter() {
729 let key = document_type.serialize_value_for_key(
730 self.field.as_str(),
731 value,
732 platform_version,
733 )?;
734 query.insert_key(key)
735 }
736 }
737 Some((starts_at_key, included)) => {
738 for value in in_values.iter() {
739 let key = document_type.serialize_value_for_key(
740 self.field.as_str(),
741 value,
742 platform_version,
743 )?;
744
745 if (left_to_right && starts_at_key < key)
746 || (!left_to_right && starts_at_key > key)
747 || (included && starts_at_key == key)
748 {
749 query.insert_key(key);
750 }
751 }
752 }
753 }
754 }
755 GreaterThan => {
756 let key = document_type.serialize_value_for_key(
757 self.field.as_str(),
758 &self.value,
759 platform_version,
760 )?;
761 match starts_at_key_option {
762 None => query.insert_range_after(key..),
763 Some((starts_at_key, included)) => {
764 if left_to_right {
765 if starts_at_key <= key {
766 query.insert_range_after(key..);
767 } else if included {
768 query.insert_range_from(starts_at_key..);
769 } else {
770 query.insert_range_after(starts_at_key..);
771 }
772 } else if starts_at_key > key {
773 if included {
774 query.insert_range_after_to_inclusive(key..=starts_at_key);
775 } else {
776 query.insert_range_after_to(key..starts_at_key);
777 }
778 }
779 }
780 }
781 }
782 GreaterThanOrEquals => {
783 let key = document_type.serialize_value_for_key(
784 self.field.as_str(),
785 &self.value,
786 platform_version,
787 )?;
788 match starts_at_key_option {
789 None => query.insert_range_from(key..),
790 Some((starts_at_key, included)) => {
791 if left_to_right {
792 if starts_at_key < key || (included && starts_at_key == key) {
793 query.insert_range_from(key..);
794 } else if included {
795 query.insert_range_from(starts_at_key..);
796 } else {
797 query.insert_range_after(starts_at_key..);
798 }
799 } else if starts_at_key > key {
800 if included {
801 query.insert_range_inclusive(key..=starts_at_key);
802 } else {
803 query.insert_range(key..starts_at_key);
804 }
805 } else if included && starts_at_key == key {
806 query.insert_key(key);
807 }
808 }
809 }
810 }
811 LessThan => {
812 let key = document_type.serialize_value_for_key(
813 self.field.as_str(),
814 &self.value,
815 platform_version,
816 )?;
817 match starts_at_key_option {
818 None => query.insert_range_to(..key),
819 Some((starts_at_key, included)) => {
820 if left_to_right {
821 if starts_at_key < key {
822 if included {
823 query.insert_range(starts_at_key..key);
824 } else {
825 query.insert_range_after_to(starts_at_key..key);
826 }
827 }
828 } else if starts_at_key > key {
829 query.insert_range_to(..key);
830 } else if included {
831 query.insert_range_to_inclusive(..=starts_at_key);
832 } else {
833 query.insert_range_to(..starts_at_key);
834 }
835 }
836 }
837 }
838 LessThanOrEquals => {
839 let key = document_type.serialize_value_for_key(
840 self.field.as_str(),
841 &self.value,
842 platform_version,
843 )?;
844 match starts_at_key_option {
845 None => query.insert_range_to_inclusive(..=key),
846 Some((starts_at_key, included)) => {
847 if left_to_right {
848 if included && starts_at_key == key {
849 query.insert_key(key);
850 } else if starts_at_key < key {
851 if included {
852 query.insert_range_inclusive(starts_at_key..=key);
853 } else {
854 query.insert_range_after_to_inclusive(starts_at_key..=key);
855 }
856 }
857 } else if starts_at_key > key || (included && starts_at_key == key) {
858 query.insert_range_to_inclusive(..=key);
859 } else if included {
860 query.insert_range_to_inclusive(..=starts_at_key);
861 } else {
862 query.insert_range_to(..starts_at_key);
863 }
864 }
865 }
866 }
867 Between => {
868 let (left_key, right_key) =
869 self.split_value_for_between(document_type, platform_version)?;
870 match starts_at_key_option {
871 None => query.insert_range_inclusive(left_key..=right_key),
872 Some((starts_at_key, included)) => {
873 if left_to_right {
874 if starts_at_key < left_key || (included && starts_at_key == left_key) {
875 query.insert_range_inclusive(left_key..=right_key)
876 } else if starts_at_key == left_key {
877 query.insert_range_after_to_inclusive(left_key..=right_key)
878 } else if starts_at_key > left_key && starts_at_key < right_key {
879 if included {
880 query.insert_range_inclusive(starts_at_key..=right_key);
881 } else {
882 query
883 .insert_range_after_to_inclusive(starts_at_key..=right_key);
884 }
885 } else if starts_at_key == right_key && included {
886 query.insert_key(right_key);
887 }
888 } else if starts_at_key > right_key
889 || (included && starts_at_key == right_key)
890 {
891 query.insert_range_inclusive(left_key..=right_key)
892 } else if starts_at_key == right_key {
893 query.insert_range(left_key..right_key)
894 } else if starts_at_key > left_key && starts_at_key < right_key {
895 if included {
896 query.insert_range_inclusive(left_key..=starts_at_key);
897 } else {
898 query.insert_range(left_key..starts_at_key);
899 }
900 } else if starts_at_key == left_key && included {
901 query.insert_key(left_key);
902 }
903 }
904 }
905 }
906 BetweenExcludeBounds => {
907 let (left_key, right_key) =
908 self.split_value_for_between(document_type, platform_version)?;
909 match starts_at_key_option {
910 None => query.insert_range_after_to(left_key..right_key),
911 Some((starts_at_key, included)) => {
912 if left_to_right {
913 if starts_at_key <= left_key {
914 query.insert_range_after_to(left_key..right_key)
915 } else if starts_at_key > left_key && starts_at_key < right_key {
916 if included {
917 query.insert_range(starts_at_key..right_key);
918 } else {
919 query.insert_range_after_to(starts_at_key..right_key);
920 }
921 }
922 } else if starts_at_key > right_key {
923 query.insert_range_inclusive(left_key..=right_key)
924 } else if starts_at_key == right_key {
925 query.insert_range(left_key..right_key)
926 } else if starts_at_key > left_key && starts_at_key < right_key {
927 if included {
928 query.insert_range_after_to_inclusive(left_key..=starts_at_key);
929 } else {
930 query.insert_range_after_to(left_key..starts_at_key);
931 }
932 }
933 }
934 }
935 }
936 BetweenExcludeLeft => {
937 let (left_key, right_key) =
938 self.split_value_for_between(document_type, platform_version)?;
939 match starts_at_key_option {
940 None => query.insert_range_after_to_inclusive(left_key..=right_key),
941 Some((starts_at_key, included)) => {
942 if left_to_right {
943 if starts_at_key <= left_key {
944 query.insert_range_after_to_inclusive(left_key..=right_key)
945 } else if starts_at_key > left_key && starts_at_key < right_key {
946 if included {
947 query.insert_range_inclusive(starts_at_key..=right_key);
948 } else {
949 query
950 .insert_range_after_to_inclusive(starts_at_key..=right_key);
951 }
952 } else if starts_at_key == right_key && included {
953 query.insert_key(right_key);
954 }
955 } else if starts_at_key > right_key
956 || (included && starts_at_key == right_key)
957 {
958 query.insert_range_after_to_inclusive(left_key..=right_key)
959 } else if starts_at_key > left_key && starts_at_key < right_key {
960 if included {
961 query.insert_range_inclusive(left_key..=starts_at_key);
962 } else {
963 query.insert_range(left_key..starts_at_key);
964 }
965 }
966 }
967 }
968 }
969 BetweenExcludeRight => {
970 let (left_key, right_key) =
971 self.split_value_for_between(document_type, platform_version)?;
972 match starts_at_key_option {
973 None => query.insert_range(left_key..right_key),
974 Some((starts_at_key, included)) => {
975 if left_to_right {
976 if starts_at_key < left_key || (included && starts_at_key == left_key) {
977 query.insert_range(left_key..right_key)
978 } else if starts_at_key == left_key {
979 query.insert_range_after_to(left_key..right_key)
980 } else if starts_at_key > left_key && starts_at_key < right_key {
981 if included {
982 query.insert_range(starts_at_key..right_key);
983 } else {
984 query.insert_range_after_to(starts_at_key..right_key);
985 }
986 }
987 } else if starts_at_key >= right_key {
988 query.insert_range(left_key..right_key)
989 } else if starts_at_key > left_key && starts_at_key < right_key {
990 if included {
991 query.insert_range_inclusive(left_key..=starts_at_key);
992 } else {
993 query.insert_range(left_key..starts_at_key);
994 }
995 } else if starts_at_key == left_key && included {
996 query.insert_key(left_key);
997 }
998 }
999 }
1000 }
1001 StartsWith => {
1002 let left_key = document_type.serialize_value_for_key(
1003 self.field.as_str(),
1004 &self.value,
1005 platform_version,
1006 )?;
1007 let mut right_key = left_key.clone();
1008 let last_char = right_key.last_mut().ok_or({
1009 Error::Query(QuerySyntaxError::InvalidStartsWithClause(
1010 "starts with must have at least one character",
1011 ))
1012 })?;
1013 *last_char += 1;
1014 match starts_at_key_option {
1015 None => query.insert_range(left_key..right_key),
1016 Some((starts_at_key, included)) => {
1017 if left_to_right {
1018 if starts_at_key < left_key || (included && starts_at_key == left_key) {
1019 query.insert_range(left_key..right_key)
1020 } else if starts_at_key == left_key {
1021 query.insert_range_after_to(left_key..right_key)
1022 } else if starts_at_key > left_key && starts_at_key < right_key {
1023 if included {
1024 query.insert_range(starts_at_key..right_key);
1025 } else {
1026 query.insert_range_after_to(starts_at_key..right_key);
1027 }
1028 }
1029 } else if starts_at_key >= right_key {
1030 query.insert_range(left_key..right_key)
1031 } else if starts_at_key > left_key && starts_at_key < right_key {
1032 if included {
1033 query.insert_range_inclusive(left_key..=starts_at_key);
1034 } else {
1035 query.insert_range(left_key..starts_at_key);
1036 }
1037 } else if starts_at_key == left_key && included {
1038 query.insert_key(left_key);
1039 }
1040 }
1041 }
1042 }
1043 Contains | ContainsAny => {
1045 return Err(Error::Drive(DriveError::CorruptedCodeExecution(
1046 "an element operator keys as an equality or an in",
1047 )))
1048 }
1049 }
1050 Ok(query)
1051 }
1052
1053 pub(crate) fn build_where_clauses_from_operations(
1054 binary_operation: &ast::Expr,
1055 document_type: &DocumentType,
1056 where_clauses: &mut Vec<WhereClause>,
1057 ) -> Result<(), Error> {
1058 match &binary_operation {
1059 ast::Expr::InList {
1060 expr,
1061 list,
1062 negated,
1063 } => {
1064 if *negated {
1065 return Err(Error::Query(QuerySyntaxError::Unsupported(
1066 "Invalid query: negated in clause not supported".to_string(),
1067 )));
1068 }
1069
1070 let field_name: String = if let ast::Expr::Identifier(ident) = &**expr {
1071 ident.value.clone()
1072 } else {
1073 return Err(Error::Query(QuerySyntaxError::InvalidInClause(
1074 "Invalid query: in clause should start with an identifier".to_string(),
1075 )));
1076 };
1077
1078 let property_type = if let Some(ty) = meta_field_property_type(&field_name) {
1079 Cow::Owned(ty)
1080 } else {
1081 let property = document_type
1082 .flattened_properties()
1083 .get(&field_name)
1084 .ok_or_else(|| {
1085 Error::Query(QuerySyntaxError::InvalidSQL(format!(
1086 "Invalid query: property named {} not in document type",
1087 field_name
1088 )))
1089 })?;
1090 Cow::Borrowed(&property.property_type)
1091 };
1092
1093 let mut in_values: Vec<Value> = Vec::new();
1094 for value in list {
1095 if let ast::Expr::Value(sql_value) = value {
1096 let platform_value =
1097 sql_value_to_platform_value(sql_value.clone()).ok_or({
1098 Error::Query(QuerySyntaxError::InvalidSQL(
1099 "Invalid query: unexpected value type".to_string(),
1100 ))
1101 })?;
1102 let transformed_value = if let Value::Text(text_value) = &platform_value {
1103 property_type.value_from_string(text_value)?
1104 } else {
1105 platform_value
1106 };
1107
1108 in_values.push(transformed_value);
1109 } else {
1110 return Err(Error::Query(QuerySyntaxError::InvalidSQL(
1111 "Invalid query: expected a list of sql values".to_string(),
1112 )));
1113 }
1114 }
1115
1116 where_clauses.push(WhereClause {
1117 field: field_name,
1118 operator: In,
1119 value: Value::Array(in_values),
1120 });
1121
1122 Ok(())
1123 }
1124 ast::Expr::Like {
1125 negated,
1126 expr,
1127 pattern,
1128 escape_char: _,
1129 } => {
1130 let where_operator = StartsWith;
1131 if *negated {
1132 return Err(Error::Query(QuerySyntaxError::Unsupported(
1133 "Negated Like not supported".to_string(),
1134 )));
1135 }
1136
1137 let field_name: String = if let ast::Expr::Identifier(ident) = &**expr {
1138 ident.value.clone()
1139 } else {
1140 panic!("unreachable: confirmed it's identifier variant");
1141 };
1142
1143 let transformed_value = if let ast::Expr::Value(value) = &**pattern {
1144 let platform_value = sql_value_to_platform_value(value.clone()).ok_or({
1145 Error::Query(QuerySyntaxError::InvalidSQL(
1146 "Invalid query: unexpected value type".to_string(),
1147 ))
1148 })?;
1149
1150 let inner_text = platform_value.as_text().ok_or({
1152 Error::Query(QuerySyntaxError::InvalidStartsWithClause(
1153 "Invalid query: startsWith takes text",
1154 ))
1155 })?;
1156 let match_locations: Vec<_> = inner_text.match_indices('%').collect();
1157 if match_locations.len() == 1 && match_locations[0].0 == inner_text.len() - 1 {
1158 Value::Text(String::from(&inner_text[..(inner_text.len() - 1)]))
1159 } else {
1160 return Err(Error::Query(QuerySyntaxError::Unsupported(
1161 "Invalid query: like can only be used to represent startswith"
1162 .to_string(),
1163 )));
1164 }
1165 } else {
1166 panic!("unreachable: confirmed it's value variant");
1167 };
1168
1169 where_clauses.push(WhereClause {
1170 field: field_name,
1171 operator: where_operator,
1172 value: transformed_value,
1173 });
1174 Ok(())
1175 }
1176 ast::Expr::BinaryOp { left, op, right } => {
1177 if *op == ast::BinaryOperator::And {
1178 Self::build_where_clauses_from_operations(left, document_type, where_clauses)?;
1179 Self::build_where_clauses_from_operations(right, document_type, where_clauses)?;
1180 } else {
1181 let mut where_operator =
1182 WhereOperator::from_sql_operator(op.clone()).ok_or(Error::Query(
1183 QuerySyntaxError::Unsupported("Unknown operator".to_string()),
1184 ))?;
1185
1186 let identifier;
1187 let value_expr;
1188
1189 if matches!(&**left, ast::Expr::Identifier(_))
1190 && matches!(&**right, ast::Expr::Value(_))
1191 {
1192 identifier = &**left;
1193 value_expr = &**right;
1194 } else if matches!(&**right, ast::Expr::Identifier(_))
1195 && matches!(&**left, ast::Expr::Value(_))
1196 {
1197 identifier = &**right;
1198 value_expr = &**left;
1199 where_operator = where_operator.flip()?;
1200 } else {
1201 return Err(Error::Query(QuerySyntaxError::InvalidSQL(
1202 "Invalid query: where clause should have field name and value"
1203 .to_string(),
1204 )));
1205 }
1206
1207 let field_name: String = if let ast::Expr::Identifier(ident) = identifier {
1208 ident.value.clone()
1209 } else {
1210 panic!("unreachable: confirmed it's identifier variant");
1211 };
1212
1213 let property_type = if let Some(ty) = meta_field_property_type(&field_name) {
1214 Cow::Owned(ty)
1215 } else {
1216 let property = document_type
1217 .flattened_properties()
1218 .get(&field_name)
1219 .ok_or_else(|| {
1220 Error::Query(QuerySyntaxError::InvalidSQL(format!(
1221 "Invalid query: property named {} not in document type",
1222 field_name
1223 )))
1224 })?;
1225 Cow::Borrowed(&property.property_type)
1226 };
1227
1228 let transformed_value = if let ast::Expr::Value(value) = value_expr {
1229 let platform_value = sql_value_to_platform_value(value.clone()).ok_or({
1230 Error::Query(QuerySyntaxError::InvalidSQL(
1231 "Invalid query: unexpected value type".to_string(),
1232 ))
1233 })?;
1234
1235 if let Value::Text(text_value) = &platform_value {
1236 property_type.value_from_string(text_value)?
1237 } else {
1238 platform_value
1239 }
1240 } else {
1241 panic!("unreachable: confirmed it's value variant");
1242 };
1243
1244 where_clauses.push(WhereClause {
1245 field: field_name,
1246 operator: where_operator,
1247 value: transformed_value,
1248 });
1249 }
1250 Ok(())
1251 }
1252 _ => Err(Error::Query(QuerySyntaxError::InvalidSQL(
1253 "Issue parsing sql: invalid selection format".to_string(),
1254 ))),
1255 }
1256 }
1257
1258 pub fn matches_value(&self, value: &Value) -> bool {
1260 self.operator.eval(value, &self.value)
1261 }
1262
1263 #[cfg(any(feature = "server", feature = "verify"))]
1272 pub fn validate_array_element_clause(
1273 &self,
1274 document_type: DocumentTypeRef,
1275 platform_version: &PlatformVersion,
1276 ) -> Result<(), Error> {
1277 let names_typed_array =
1278 typed_array_property(document_type.flattened_properties(), &self.field).is_some();
1279 if let Some(error) = self.element_operator_mismatch(names_typed_array) {
1280 return Err(Error::Query(error));
1281 }
1282 if !names_typed_array {
1283 return Ok(());
1284 }
1285 self.validate_element_values(|value| {
1286 document_type
1287 .serialize_value_for_key(&self.field, value, platform_version)
1288 .is_ok()
1289 })
1290 .map_err(Error::Query)
1291 }
1292
1293 #[cfg(any(feature = "server", feature = "verify"))]
1296 fn element_operator_mismatch(&self, names_typed_array: bool) -> Option<QuerySyntaxError> {
1297 (names_typed_array != self.operator.reads_array_elements()).then_some(
1298 QuerySyntaxError::InvalidWhereClauseComponents(if names_typed_array {
1299 "a typed array is read with contains or containsAny, and no other operator"
1300 } else {
1301 "contains and containsAny read the elements of a typed array"
1302 }),
1303 )
1304 }
1305
1306 #[cfg(any(feature = "server", feature = "verify"))]
1314 fn validate_element_values(
1315 &self,
1316 keys_as_element: impl Fn(&Value) -> bool,
1317 ) -> Result<(), QuerySyntaxError> {
1318 let listed;
1319 let values: &[Value] = if self.operator == ContainsAny {
1320 let in_values = self.in_values();
1321 if let Some(error) = in_values.errors.into_iter().next() {
1322 return Err(error);
1323 }
1324 listed = in_values.data.unwrap_or_default();
1325 &listed
1326 } else {
1327 std::slice::from_ref(&self.value)
1328 };
1329 if values
1330 .iter()
1331 .all(|value| !value.is_null() && keys_as_element(value))
1332 {
1333 Ok(())
1334 } else {
1335 Err(QuerySyntaxError::InvalidWhereClauseComponents(
1336 "invalid value type for an element of the typed array",
1337 ))
1338 }
1339 }
1340
1341 #[cfg(any(feature = "server", feature = "verify"))]
1343 pub fn validate_against_schema(
1344 &self,
1345 document_type: DocumentTypeRef,
1346 ) -> QuerySyntaxSimpleValidationResult {
1347 let property_type_cow = if let Some(meta_ty) = meta_field_property_type(&self.field) {
1349 Cow::Owned(meta_ty)
1350 } else {
1351 let Some(property) = document_type.flattened_properties().get(&self.field) else {
1353 return QuerySyntaxSimpleValidationResult::new_with_error(
1354 QuerySyntaxError::InvalidWhereClauseComponents("unknown field in where clause"),
1355 );
1356 };
1357 Cow::Borrowed(&property.property_type)
1358 };
1359
1360 let property_type = property_type_cow.as_ref();
1366 let element_type = match property_type {
1367 DocumentPropertyType::TypedArray(typed_array) => Some(&typed_array.item_type),
1368 _ => None,
1369 };
1370 if let Some(error) = self.element_operator_mismatch(element_type.is_some()) {
1371 return QuerySyntaxSimpleValidationResult::new_with_error(error);
1372 }
1373 if let Some(element_type) = element_type {
1374 return match self.validate_element_values(|value| {
1375 element_type
1376 .encode_value_for_tree_keys(value)
1377 .is_ok_and(|key| key.len() <= MAX_INDEX_SIZE)
1378 }) {
1379 Ok(()) => QuerySyntaxSimpleValidationResult::new(),
1380 Err(error) => QuerySyntaxSimpleValidationResult::new_with_error(error),
1381 };
1382 }
1383
1384 self.validate_against_property_type(property_type)
1385 }
1386
1387 #[cfg(any(feature = "server", feature = "verify"))]
1390 fn validate_against_property_type(
1391 &self,
1392 property_type: &DocumentPropertyType,
1393 ) -> QuerySyntaxSimpleValidationResult {
1394 if !allowed_ops_for_type(property_type).contains(&self.operator) {
1395 return QuerySyntaxSimpleValidationResult::new_with_error(
1396 QuerySyntaxError::InvalidWhereClauseComponents(
1397 "operator not allowed for field type",
1398 ),
1399 );
1400 }
1401
1402 if self.operator == StartsWith {
1404 if let Value::Text(s) = &self.value {
1405 if s.is_empty() {
1406 return QuerySyntaxSimpleValidationResult::new_with_error(
1407 QuerySyntaxError::StartsWithIllegalString(
1408 "starts_with can not start with an empty string",
1409 ),
1410 );
1411 }
1412 }
1413 }
1414
1415 if self.operator == In {
1417 let result = self.in_values();
1419 if !result.is_valid() {
1420 return QuerySyntaxSimpleValidationResult::new_with_errors(result.errors);
1421 }
1422 if matches!(self.value, Value::Bytes(_))
1424 && !matches!(property_type, DocumentPropertyType::U8)
1425 {
1426 return QuerySyntaxSimpleValidationResult::new_with_error(
1427 QuerySyntaxError::InvalidWhereClauseComponents(
1428 "IN Bytes only allowed for U8 fields",
1429 ),
1430 );
1431 }
1432 }
1433
1434 if !self.operator.value_shape_ok(&self.value, property_type) {
1436 return QuerySyntaxSimpleValidationResult::new_with_error(
1437 QuerySyntaxError::InvalidWhereClauseComponents("invalid value shape for operator"),
1438 );
1439 }
1440
1441 match self.operator {
1443 Between | BetweenExcludeBounds | BetweenExcludeLeft | BetweenExcludeRight => {
1444 if let Value::Array(bounds) = &self.value {
1445 if bounds.len() == 2 {
1446 match bounds[0].partial_cmp(&bounds[1]) {
1447 Some(Ordering::Less) => {}
1448 _ => {
1449 return QuerySyntaxSimpleValidationResult::new_with_error(
1450 QuerySyntaxError::InvalidBetweenClause(
1451 "when using between operator bounds must be strictly ascending",
1452 ),
1453 );
1454 }
1455 }
1456 }
1457 }
1458 }
1459 _ => {}
1460 }
1461
1462 let value_type_matches = |prop_ty: &DocumentPropertyType, v: &Value| -> bool {
1464 use DocumentPropertyType as T;
1465 match prop_ty {
1466 T::String(_) => matches!(v, Value::Text(_)),
1467 T::Identifier | T::IdentifierWithReference(_) => matches!(v, Value::Identifier(_)),
1468 T::Boolean => matches!(v, Value::Bool(_)),
1469 T::ByteArray(_) => matches!(v, Value::Bytes(_)),
1470 T::F64 => matches!(v, Value::Float(_)),
1471 T::Date => matches!(
1472 v,
1473 Value::U64(_)
1474 | Value::I64(_)
1475 | Value::U32(_)
1476 | Value::I32(_)
1477 | Value::U16(_)
1478 | Value::I16(_)
1479 | Value::U8(_)
1480 | Value::I8(_)
1481 ),
1482 T::U8 | T::U16 | T::U32 | T::KeyIdWithReference(_) | T::U64 | T::U128 => {
1483 matches!(
1484 v,
1485 Value::U8(_)
1486 | Value::U16(_)
1487 | Value::U32(_)
1488 | Value::U64(_)
1489 | Value::U128(_)
1490 )
1491 }
1492 T::I8 | T::I16 | T::I32 | T::I64 | T::I128 => matches!(
1493 v,
1494 Value::I8(_) | Value::I16(_) | Value::I32(_) | Value::I64(_) | Value::I128(_)
1495 ),
1496 T::Object(_) | T::Array(_) | T::VariableTypeArray(_) | T::TypedArray(_) => false,
1498 }
1499 };
1500
1501 match self.operator {
1503 Equal => {
1504 use DocumentPropertyType as T;
1505 let ok = match property_type {
1506 T::U8
1508 | T::U16
1509 | T::U32
1510 | T::KeyIdWithReference(_)
1511 | T::U64
1512 | T::U128
1513 | T::I8
1514 | T::I16
1515 | T::I32
1516 | T::I64
1517 | T::I128 => {
1518 matches!(
1519 self.value,
1520 Value::U128(_)
1521 | Value::I128(_)
1522 | Value::U64(_)
1523 | Value::I64(_)
1524 | Value::U32(_)
1525 | Value::I32(_)
1526 | Value::U16(_)
1527 | Value::I16(_)
1528 | Value::U8(_)
1529 | Value::I8(_)
1530 )
1531 }
1532 T::F64 => matches!(self.value, Value::Float(_)),
1533 T::Date => matches!(
1534 self.value,
1535 Value::U64(_)
1536 | Value::I64(_)
1537 | Value::U32(_)
1538 | Value::I32(_)
1539 | Value::U16(_)
1540 | Value::I16(_)
1541 | Value::U8(_)
1542 | Value::I8(_)
1543 ),
1544 T::String(_) => matches!(self.value, Value::Text(_)),
1545 T::Identifier | T::IdentifierWithReference(_) => {
1546 matches!(self.value, Value::Identifier(_))
1547 }
1548 T::ByteArray(_) => matches!(self.value, Value::Bytes(_)),
1549 T::Boolean => matches!(self.value, Value::Bool(_)),
1550 T::Object(_) | T::Array(_) | T::VariableTypeArray(_) | T::TypedArray(_) => {
1552 false
1553 }
1554 };
1555 if !ok {
1556 return QuerySyntaxSimpleValidationResult::new_with_error(
1557 QuerySyntaxError::InvalidWhereClauseComponents(
1558 "invalid value type for equality",
1559 ),
1560 );
1561 }
1562 }
1563 In => {
1564 if let Value::Array(arr) = &self.value {
1565 if !arr.iter().all(|v| value_type_matches(property_type, v)) {
1566 return QuerySyntaxSimpleValidationResult::new_with_error(
1567 QuerySyntaxError::InvalidWhereClauseComponents(
1568 "invalid value type in IN clause",
1569 ),
1570 );
1571 }
1572 }
1573 }
1574 _ => {}
1575 }
1576
1577 QuerySyntaxSimpleValidationResult::new()
1578 }
1579}
1580
1581impl From<WhereClause> for Value {
1582 fn from(value: WhereClause) -> Self {
1583 Value::Array(vec![value.field.into(), value.operator.into(), value.value])
1584 }
1585}
1586
1587#[derive(Clone, Debug, PartialEq)]
1589#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
1590pub struct ValueClause {
1591 pub operator: WhereOperator,
1593 pub value: Value,
1595}
1596
1597impl ValueClause {
1598 pub fn matches_value(&self, value: &Value) -> bool {
1600 self.operator.eval(value, &self.value)
1601 }
1602}
1603
1604#[cfg(any(feature = "server", feature = "verify"))]
1606pub fn allowed_ops_for_type(property_type: &DocumentPropertyType) -> &'static [WhereOperator] {
1607 match property_type {
1608 DocumentPropertyType::U8
1609 | DocumentPropertyType::I8
1610 | DocumentPropertyType::U16
1611 | DocumentPropertyType::I16
1612 | DocumentPropertyType::U32
1613 | DocumentPropertyType::KeyIdWithReference(_)
1614 | DocumentPropertyType::I32
1615 | DocumentPropertyType::U64
1616 | DocumentPropertyType::I64
1617 | DocumentPropertyType::U128
1618 | DocumentPropertyType::I128
1619 | DocumentPropertyType::F64
1620 | DocumentPropertyType::Date => &[
1621 Equal,
1622 In,
1623 GreaterThan,
1624 GreaterThanOrEquals,
1625 LessThan,
1626 LessThanOrEquals,
1627 Between,
1628 BetweenExcludeBounds,
1629 BetweenExcludeLeft,
1630 BetweenExcludeRight,
1631 ],
1632 DocumentPropertyType::String(_) => &[
1633 Equal,
1634 In,
1635 StartsWith,
1636 GreaterThan,
1637 GreaterThanOrEquals,
1638 LessThan,
1639 LessThanOrEquals,
1640 Between,
1641 BetweenExcludeBounds,
1642 BetweenExcludeLeft,
1643 BetweenExcludeRight,
1644 ],
1645 DocumentPropertyType::Identifier | DocumentPropertyType::IdentifierWithReference(_) => {
1646 &[Equal, In]
1647 }
1648 DocumentPropertyType::ByteArray(_) => &[Equal, In],
1649 DocumentPropertyType::Boolean => &[Equal],
1650 DocumentPropertyType::TypedArray(_) => &[Contains, ContainsAny],
1653 DocumentPropertyType::Object(_)
1654 | DocumentPropertyType::Array(_)
1655 | DocumentPropertyType::VariableTypeArray(_) => &[],
1656 }
1657}
1658
1659#[cfg(any(feature = "server", feature = "verify"))]
1660fn is_numeric_value(value: &Value) -> bool {
1661 matches!(
1662 value,
1663 Value::U128(_)
1664 | Value::I128(_)
1665 | Value::U64(_)
1666 | Value::I64(_)
1667 | Value::U32(_)
1668 | Value::I32(_)
1669 | Value::U16(_)
1670 | Value::I16(_)
1671 | Value::U8(_)
1672 | Value::I8(_)
1673 | Value::Float(_)
1674 )
1675}
1676
1677fn meta_field_property_type(field: &str) -> Option<DocumentPropertyType> {
1680 match field {
1681 "$id" | "$ownerId" | "$dataContractId" | "$creatorId" | "$moderatedBy" => {
1683 Some(DocumentPropertyType::Identifier)
1684 }
1685 "$createdAt" | "$updatedAt" | "$transferredAt" | "$moderatedAt" => {
1687 Some(DocumentPropertyType::Date)
1688 }
1689 "$createdAtBlockHeight" | "$updatedAtBlockHeight" | "$transferredAtBlockHeight" => {
1691 Some(DocumentPropertyType::U64)
1692 }
1693 "$createdAtCoreBlockHeight"
1694 | "$updatedAtCoreBlockHeight"
1695 | "$transferredAtCoreBlockHeight" => Some(DocumentPropertyType::U32),
1696 "$revision" | "$protocolVersion" => Some(DocumentPropertyType::U64),
1698 "$type" => Some(DocumentPropertyType::String(
1700 dpp::data_contract::document_type::StringPropertySizes {
1701 min_length: None,
1702 max_length: None,
1703 max_bytes: None,
1704 },
1705 )),
1706 _ => None,
1707 }
1708}
1709
1710#[cfg(feature = "server")]
1711#[cfg(test)]
1712#[allow(clippy::approx_constant)]
1713mod tests {
1714 use crate::error::query::QuerySyntaxError;
1715 use crate::query::conditions::WhereClause;
1716 use crate::query::conditions::{
1717 Between, BetweenExcludeBounds, BetweenExcludeLeft, BetweenExcludeRight, Equal, GreaterThan,
1718 GreaterThanOrEquals, In, LessThan, LessThanOrEquals, ValueClause,
1719 };
1720 use crate::query::InternalClauses;
1721 use dpp::data_contract::accessors::v0::DataContractV0Getters;
1722 use dpp::data_contract::document_type::methods::DocumentTypeV0Methods;
1723 use dpp::document::DocumentV0;
1724 use dpp::platform_value::Value;
1725 use dpp::prelude::Identifier;
1726 use dpp::tests::fixtures::get_data_contract_fixture;
1727 use dpp::version::PlatformVersion;
1728 use dpp::version::LATEST_PLATFORM_VERSION;
1729 use grovedb::Query;
1730 use std::collections::BTreeMap;
1731
1732 fn cursor_document(field: &str, value: Value) -> dpp::document::Document {
1733 DocumentV0 {
1734 contract_version: None,
1735 id: Identifier::from([3u8; 32]),
1736 owner_id: Identifier::from([4u8; 32]),
1737 properties: BTreeMap::from([(field.to_string(), value)]),
1738 revision: None,
1739 created_at: None,
1740 updated_at: None,
1741 transferred_at: None,
1742 created_at_block_height: None,
1743 updated_at_block_height: None,
1744 transferred_at_block_height: None,
1745 created_at_core_block_height: None,
1746 updated_at_core_block_height: None,
1747 transferred_at_core_block_height: None,
1748 creator_id: None,
1749 moderated_at: None,
1750 moderated_by: None,
1751 }
1752 .into()
1753 }
1754
1755 #[test]
1756 fn ascending_less_than_ranges_start_at_the_cursor() {
1757 let fixture = get_data_contract_fixture(None, 0, LATEST_PLATFORM_VERSION.protocol_version);
1758 let contract = fixture.data_contract_owned();
1759 let document_type = contract
1760 .document_type_for_name("niceDocument")
1761 .expect("document type exists");
1762 let cursor_value = Value::Text("m".to_string());
1763 let upper_value = Value::Text("z".to_string());
1764 let cursor_key = document_type
1765 .serialize_value_for_key("name", &cursor_value, LATEST_PLATFORM_VERSION)
1766 .unwrap();
1767 let upper_key = document_type
1768 .serialize_value_for_key("name", &upper_value, LATEST_PLATFORM_VERSION)
1769 .unwrap();
1770
1771 for (operator, cursor_included) in [
1772 (LessThan, true),
1773 (LessThan, false),
1774 (LessThanOrEquals, true),
1775 (LessThanOrEquals, false),
1776 ] {
1777 let clause = WhereClause {
1778 field: "name".to_string(),
1779 operator,
1780 value: upper_value.clone(),
1781 };
1782 let start_at = Some((
1783 cursor_document("name", cursor_value.clone()),
1784 cursor_included,
1785 ));
1786 let actual = clause
1787 .to_path_query(document_type, &start_at, true, LATEST_PLATFORM_VERSION)
1788 .unwrap();
1789 let mut expected = Query::new_with_direction(true);
1790
1791 match (operator, cursor_included) {
1792 (LessThan, true) => expected.insert_range(cursor_key.clone()..upper_key.clone()),
1793 (LessThan, false) => {
1794 expected.insert_range_after_to(cursor_key.clone()..upper_key.clone())
1795 }
1796 (LessThanOrEquals, true) => {
1797 expected.insert_range_inclusive(cursor_key.clone()..=upper_key.clone())
1798 }
1799 (LessThanOrEquals, false) => {
1800 expected.insert_range_after_to_inclusive(cursor_key.clone()..=upper_key.clone())
1801 }
1802 _ => unreachable!(),
1803 }
1804
1805 assert_eq!(actual.items, expected.items);
1806 }
1807 }
1808
1809 #[test]
1810 fn test_allowed_sup_query_pairs() {
1811 let allowed_pairs_test_cases = [
1812 [GreaterThan, LessThan],
1813 [GreaterThan, LessThanOrEquals],
1814 [GreaterThanOrEquals, LessThanOrEquals],
1815 ];
1816 for query_pair in allowed_pairs_test_cases {
1817 let where_clauses = vec![
1818 WhereClause {
1819 field: "a".to_string(),
1820 operator: *query_pair.first().unwrap(),
1821 value: Value::Float(0.0),
1822 },
1823 WhereClause {
1824 field: "a".to_string(),
1825 operator: *query_pair.get(1).unwrap(),
1826 value: Value::Float(1.0),
1827 },
1828 ];
1829 let (_, range_clause, _) =
1830 WhereClause::group_clauses(&where_clauses, PlatformVersion::latest())
1831 .expect("expected to have groupable pair");
1832 range_clause.expect("expected to have range clause returned");
1833 }
1834 }
1835
1836 #[test]
1837 fn test_allowed_inf_query_pairs() {
1838 let allowed_pairs_test_cases = [
1839 [LessThan, GreaterThan],
1840 [LessThan, GreaterThanOrEquals],
1841 [LessThanOrEquals, GreaterThanOrEquals],
1842 ];
1843 for query_pair in allowed_pairs_test_cases {
1844 let where_clauses = vec![
1845 WhereClause {
1846 field: "a".to_string(),
1847 operator: *query_pair.first().unwrap(),
1848 value: Value::Float(1.0),
1849 },
1850 WhereClause {
1851 field: "a".to_string(),
1852 operator: *query_pair.get(1).unwrap(),
1853 value: Value::Float(0.0),
1854 },
1855 ];
1856 let (_, range_clause, _) =
1857 WhereClause::group_clauses(&where_clauses, PlatformVersion::latest())
1858 .expect("expected to have groupable pair");
1859 range_clause.expect("expected to have range clause returned");
1860 }
1861 }
1862
1863 #[test]
1864 fn test_query_pairs_incoherent_same_value() {
1865 let allowed_pairs_test_cases = [[LessThan, GreaterThan], [GreaterThan, LessThan]];
1866 for query_pair in allowed_pairs_test_cases {
1867 let where_clauses = vec![
1868 WhereClause {
1869 field: "a".to_string(),
1870 operator: *query_pair.first().unwrap(),
1871 value: Value::Float(1.0),
1872 },
1873 WhereClause {
1874 field: "a".to_string(),
1875 operator: *query_pair.get(1).unwrap(),
1876 value: Value::Float(1.0),
1877 },
1878 ];
1879 WhereClause::group_clauses(&where_clauses, PlatformVersion::latest())
1880 .expect_err("expected to have an error returned");
1881 }
1882 }
1883
1884 #[test]
1885 fn test_different_fields_grouping_causes_error() {
1886 let where_clauses = vec![
1887 WhereClause {
1888 field: "a".to_string(),
1889 operator: LessThan,
1890 value: Value::Float(0.0),
1891 },
1892 WhereClause {
1893 field: "b".to_string(),
1894 operator: GreaterThan,
1895 value: Value::Float(1.0),
1896 },
1897 ];
1898 WhereClause::group_clauses(&where_clauses, PlatformVersion::latest())
1899 .expect_err("different fields should not be groupable");
1900 }
1901
1902 #[test]
1903 fn test_restricted_query_pairs_causes_error() {
1904 let restricted_pairs_test_cases = [
1905 [Equal, LessThan],
1906 [Equal, GreaterThan],
1907 [In, LessThan],
1908 [Equal, GreaterThan],
1909 [LessThanOrEquals, LessThanOrEquals],
1910 [LessThan, LessThan],
1911 [LessThan, LessThanOrEquals],
1912 [GreaterThan, GreaterThan],
1913 [GreaterThan, GreaterThanOrEquals],
1914 [GreaterThanOrEquals, GreaterThanOrEquals],
1915 [Equal, Equal],
1916 ];
1917 for query_pair in restricted_pairs_test_cases {
1918 let where_clauses = vec![
1919 WhereClause {
1920 field: "a".to_string(),
1921 operator: *query_pair.first().unwrap(),
1922 value: Value::Float(0.0),
1923 },
1924 WhereClause {
1925 field: "a".to_string(),
1926 operator: *query_pair.get(1).unwrap(),
1927 value: Value::Float(1.0),
1928 },
1929 ];
1930 WhereClause::group_clauses(&where_clauses, PlatformVersion::latest())
1931 .expect_err("expected to not have a groupable pair");
1932 }
1933 }
1934
1935 #[test]
1936 fn validate_rejects_equality_with_wrong_type_for_string_field() {
1937 let fixture = get_data_contract_fixture(None, 0, LATEST_PLATFORM_VERSION.protocol_version);
1938 let contract = fixture.data_contract_owned();
1939 let doc_type = contract
1940 .document_type_for_name("niceDocument")
1941 .expect("doc type exists");
1942
1943 let clause = WhereClause {
1944 field: "name".to_string(),
1945 operator: Equal,
1946 value: Value::Identifier([1u8; 32]),
1947 };
1948 let res = clause.validate_against_schema(doc_type);
1949 assert!(res.is_err());
1950 assert!(matches!(
1951 res.first_error(),
1952 Some(QuerySyntaxError::InvalidWhereClauseComponents(_))
1953 ));
1954 }
1955
1956 #[test]
1957 fn validate_rejects_in_with_wrong_element_types() {
1958 let fixture = get_data_contract_fixture(None, 0, LATEST_PLATFORM_VERSION.protocol_version);
1959 let contract = fixture.data_contract_owned();
1960 let doc_type = contract
1961 .document_type_for_name("indexedDocument")
1962 .expect("doc type exists");
1963
1964 let clause = WhereClause {
1965 field: "firstName".to_string(),
1966 operator: In,
1967 value: Value::Array(vec![
1968 Value::Text("alice".to_string()),
1969 Value::Identifier([2u8; 32]),
1970 ]),
1971 };
1972 let res = clause.validate_against_schema(doc_type);
1973 assert!(res.is_err());
1974 assert!(matches!(
1975 res.first_error(),
1976 Some(QuerySyntaxError::InvalidWhereClauseComponents(_))
1977 ));
1978 }
1979
1980 #[test]
1981 fn validate_rejects_primary_key_in_with_non_identifiers() {
1982 let fixture = get_data_contract_fixture(None, 0, LATEST_PLATFORM_VERSION.protocol_version);
1983 let contract = fixture.data_contract_owned();
1984 let doc_type = contract
1985 .document_type_for_name("niceDocument")
1986 .expect("doc type exists");
1987
1988 let clauses = InternalClauses {
1989 primary_key_in_clause: Some(WhereClause {
1990 field: "$id".to_string(),
1991 operator: In,
1992 value: Value::Array(vec![
1993 Value::Text("a".to_string()),
1994 Value::Text("b".to_string()),
1995 ]),
1996 }),
1997 ..Default::default()
1998 };
1999
2000 let res = clauses.validate_against_schema(doc_type);
2001 assert!(res.is_err());
2002 assert!(matches!(
2003 res.first_error(),
2004 Some(QuerySyntaxError::InvalidWhereClauseComponents(_))
2005 ));
2006 }
2007
2008 #[test]
2009 fn validate_rejects_date_with_float_equality() {
2010 let fixture = get_data_contract_fixture(None, 0, LATEST_PLATFORM_VERSION.protocol_version);
2011 let contract = fixture.data_contract_owned();
2012 let doc_type = contract
2013 .document_type_for_name("uniqueDates")
2014 .expect("doc type exists");
2015
2016 let clause = WhereClause {
2017 field: "$createdAt".to_string(),
2018 operator: Equal,
2019 value: Value::Float(1.23),
2020 };
2021 let res = clause.validate_against_schema(doc_type);
2022 assert!(res.is_err());
2023 assert!(matches!(
2024 res.first_error(),
2025 Some(QuerySyntaxError::InvalidWhereClauseComponents(_))
2026 ));
2027 }
2028
2029 #[test]
2030 fn validate_rejects_in_bytes_for_string_field() {
2031 let fixture = get_data_contract_fixture(None, 0, LATEST_PLATFORM_VERSION.protocol_version);
2032 let contract = fixture.data_contract_owned();
2033 let doc_type = contract
2034 .document_type_for_name("niceDocument")
2035 .expect("doc type exists");
2036
2037 let clause = WhereClause {
2039 field: "name".to_string(),
2040 operator: In,
2041 value: Value::Bytes(vec![1, 2, 3]),
2042 };
2043 let res = clause.validate_against_schema(doc_type);
2044 assert!(res.is_err());
2045 }
2046
2047 #[test]
2048 fn validate_accepts_meta_owner_id_in_identifiers() {
2049 let fixture = get_data_contract_fixture(None, 0, LATEST_PLATFORM_VERSION.protocol_version);
2050 let contract = fixture.data_contract_owned();
2051 let doc_type = contract
2052 .document_type_for_name("niceDocument")
2053 .expect("doc type exists");
2054
2055 let clause = WhereClause {
2056 field: "$ownerId".to_string(),
2057 operator: In,
2058 value: Value::Array(vec![
2059 Value::Identifier([1u8; 32]),
2060 Value::Identifier([2u8; 32]),
2061 ]),
2062 };
2063 let res = clause.validate_against_schema(doc_type);
2064 assert!(res.is_valid());
2065 }
2066
2067 #[test]
2068 fn validate_accepts_meta_created_at_between_integers() {
2069 let fixture = get_data_contract_fixture(None, 0, LATEST_PLATFORM_VERSION.protocol_version);
2070 let contract = fixture.data_contract_owned();
2071 let doc_type = contract
2072 .document_type_for_name("uniqueDates")
2073 .expect("doc type exists");
2074
2075 let clause = WhereClause {
2076 field: "$createdAt".to_string(),
2077 operator: crate::query::conditions::Between,
2078 value: Value::Array(vec![Value::U64(1000), Value::U64(2000)]),
2079 };
2080 let res = clause.validate_against_schema(doc_type);
2081 assert!(res.is_valid());
2082 }
2083
2084 #[test]
2085 fn validate_rejects_between_variants_with_equal_bounds() {
2086 let fixture = get_data_contract_fixture(None, 0, LATEST_PLATFORM_VERSION.protocol_version);
2087 let contract = fixture.data_contract_owned();
2088 let doc_type = contract
2089 .document_type_for_name("uniqueDates")
2090 .expect("doc type exists");
2091
2092 for operator in [
2093 Between,
2094 BetweenExcludeBounds,
2095 BetweenExcludeLeft,
2096 BetweenExcludeRight,
2097 ] {
2098 let clause = WhereClause {
2099 field: "$createdAt".to_string(),
2100 operator,
2101 value: Value::Array(vec![Value::U64(1000), Value::U64(1000)]),
2102 };
2103
2104 let res = clause.validate_against_schema(doc_type);
2105 assert!(
2106 res.is_err(),
2107 "{operator:?} should reject equal bounds during validation"
2108 );
2109 assert!(matches!(
2110 res.first_error(),
2111 Some(QuerySyntaxError::InvalidBetweenClause(_))
2112 ));
2113 }
2114 }
2115
2116 #[test]
2117 fn value_clause_between_variants_do_not_match_equal_bounds() {
2118 let equal_bounds = Value::Array(vec![Value::U64(1000), Value::U64(1000)]);
2119 let value_to_test = Value::U64(1000);
2120
2121 for operator in [
2122 Between,
2123 BetweenExcludeBounds,
2124 BetweenExcludeLeft,
2125 BetweenExcludeRight,
2126 ] {
2127 let clause = ValueClause {
2128 operator,
2129 value: equal_bounds.clone(),
2130 };
2131
2132 assert!(
2133 !clause.matches_value(&value_to_test),
2134 "{operator:?} should not match when bounds are equal"
2135 );
2136 }
2137 }
2138
2139 #[test]
2140 fn validate_rejects_meta_revision_float_equality() {
2141 let fixture = get_data_contract_fixture(None, 0, LATEST_PLATFORM_VERSION.protocol_version);
2142 let contract = fixture.data_contract_owned();
2143 let doc_type = contract
2144 .document_type_for_name("niceDocument")
2145 .expect("doc type exists");
2146
2147 let clause = WhereClause {
2148 field: "$revision".to_string(),
2149 operator: Equal,
2150 value: Value::Float(3.15),
2151 };
2152 let res = clause.validate_against_schema(doc_type);
2153 assert!(res.is_err());
2154 }
2155
2156 #[test]
2157 fn validate_accepts_meta_created_at_block_height_range() {
2158 let fixture = get_data_contract_fixture(None, 0, LATEST_PLATFORM_VERSION.protocol_version);
2159 let contract = fixture.data_contract_owned();
2160 let doc_type = contract
2161 .document_type_for_name("uniqueDates")
2162 .expect("doc type exists");
2163
2164 let clause = WhereClause {
2165 field: "$createdAtBlockHeight".to_string(),
2166 operator: GreaterThanOrEquals,
2167 value: Value::U64(100),
2168 };
2169 let res = clause.validate_against_schema(doc_type);
2170 assert!(res.is_valid());
2171 }
2172
2173 #[test]
2174 fn validate_accepts_meta_data_contract_id_equality() {
2175 let fixture = get_data_contract_fixture(None, 0, LATEST_PLATFORM_VERSION.protocol_version);
2176 let contract = fixture.data_contract_owned();
2177 let doc_type = contract
2178 .document_type_for_name("niceDocument")
2179 .expect("doc type exists");
2180
2181 let clause = WhereClause {
2182 field: "$dataContractId".to_string(),
2183 operator: Equal,
2184 value: Value::Identifier([3u8; 32]),
2185 };
2186 let res = clause.validate_against_schema(doc_type);
2187 assert!(res.is_valid());
2188 }
2189
2190 #[test]
2193 fn allows_flip_returns_true_for_comparison_operators() {
2194 assert!(Equal.allows_flip());
2195 assert!(GreaterThan.allows_flip());
2196 assert!(GreaterThanOrEquals.allows_flip());
2197 assert!(LessThan.allows_flip());
2198 assert!(LessThanOrEquals.allows_flip());
2199 }
2200
2201 #[test]
2202 fn allows_flip_returns_false_for_non_flippable_operators() {
2203 assert!(!Between.allows_flip());
2204 assert!(!BetweenExcludeBounds.allows_flip());
2205 assert!(!BetweenExcludeLeft.allows_flip());
2206 assert!(!BetweenExcludeRight.allows_flip());
2207 assert!(!In.allows_flip());
2208 assert!(!super::StartsWith.allows_flip());
2209 }
2210
2211 #[test]
2214 fn flip_equal_stays_equal() {
2215 assert_eq!(Equal.flip().unwrap(), Equal);
2216 }
2217
2218 #[test]
2219 fn flip_greater_than_becomes_less_than() {
2220 assert_eq!(GreaterThan.flip().unwrap(), LessThan);
2221 }
2222
2223 #[test]
2224 fn flip_greater_than_or_equals_becomes_less_than_or_equals() {
2225 assert_eq!(GreaterThanOrEquals.flip().unwrap(), LessThanOrEquals);
2226 }
2227
2228 #[test]
2229 fn flip_less_than_becomes_greater_than() {
2230 assert_eq!(LessThan.flip().unwrap(), GreaterThan);
2231 }
2232
2233 #[test]
2234 fn flip_less_than_or_equals_becomes_greater_than_or_equals() {
2235 assert_eq!(LessThanOrEquals.flip().unwrap(), GreaterThanOrEquals);
2236 }
2237
2238 #[test]
2239 fn flip_between_returns_error() {
2240 assert!(Between.flip().is_err());
2241 }
2242
2243 #[test]
2244 fn flip_between_exclude_bounds_returns_error() {
2245 assert!(BetweenExcludeBounds.flip().is_err());
2246 }
2247
2248 #[test]
2249 fn flip_between_exclude_left_returns_error() {
2250 assert!(BetweenExcludeLeft.flip().is_err());
2251 }
2252
2253 #[test]
2254 fn flip_between_exclude_right_returns_error() {
2255 assert!(BetweenExcludeRight.flip().is_err());
2256 }
2257
2258 #[test]
2259 fn flip_in_returns_error() {
2260 assert!(In.flip().is_err());
2261 }
2262
2263 #[test]
2264 fn flip_starts_with_returns_error() {
2265 assert!(super::StartsWith.flip().is_err());
2266 }
2267
2268 #[test]
2271 fn is_range_false_for_equal() {
2272 assert!(!Equal.is_range());
2273 }
2274
2275 #[test]
2276 fn is_range_true_for_all_range_operators() {
2277 assert!(GreaterThan.is_range());
2278 assert!(GreaterThanOrEquals.is_range());
2279 assert!(LessThan.is_range());
2280 assert!(LessThanOrEquals.is_range());
2281 assert!(Between.is_range());
2282 assert!(BetweenExcludeBounds.is_range());
2283 assert!(BetweenExcludeLeft.is_range());
2284 assert!(BetweenExcludeRight.is_range());
2285 assert!(In.is_range());
2286 assert!(super::StartsWith.is_range());
2287 }
2288
2289 #[test]
2292 fn from_string_parses_equality_operators() {
2293 use super::WhereOperator;
2294 assert_eq!(WhereOperator::from_string("="), Some(Equal));
2295 assert_eq!(WhereOperator::from_string("=="), Some(Equal));
2296 }
2297
2298 #[test]
2299 fn from_string_parses_comparison_operators() {
2300 use super::WhereOperator;
2301 assert_eq!(WhereOperator::from_string(">"), Some(GreaterThan));
2302 assert_eq!(WhereOperator::from_string(">="), Some(GreaterThanOrEquals));
2303 assert_eq!(WhereOperator::from_string("<"), Some(LessThan));
2304 assert_eq!(WhereOperator::from_string("<="), Some(LessThanOrEquals));
2305 }
2306
2307 #[test]
2308 fn from_string_parses_between_variants() {
2309 use super::WhereOperator;
2310 assert_eq!(WhereOperator::from_string("Between"), Some(Between));
2311 assert_eq!(WhereOperator::from_string("between"), Some(Between));
2312 assert_eq!(
2313 WhereOperator::from_string("BetweenExcludeBounds"),
2314 Some(BetweenExcludeBounds)
2315 );
2316 assert_eq!(
2317 WhereOperator::from_string("betweenExcludeBounds"),
2318 Some(BetweenExcludeBounds)
2319 );
2320 assert_eq!(
2321 WhereOperator::from_string("betweenexcludebounds"),
2322 Some(BetweenExcludeBounds)
2323 );
2324 assert_eq!(
2325 WhereOperator::from_string("between_exclude_bounds"),
2326 Some(BetweenExcludeBounds)
2327 );
2328 assert_eq!(
2329 WhereOperator::from_string("BetweenExcludeLeft"),
2330 Some(BetweenExcludeLeft)
2331 );
2332 assert_eq!(
2333 WhereOperator::from_string("betweenExcludeLeft"),
2334 Some(BetweenExcludeLeft)
2335 );
2336 assert_eq!(
2337 WhereOperator::from_string("betweenexcludeleft"),
2338 Some(BetweenExcludeLeft)
2339 );
2340 assert_eq!(
2341 WhereOperator::from_string("between_exclude_left"),
2342 Some(BetweenExcludeLeft)
2343 );
2344 assert_eq!(
2345 WhereOperator::from_string("BetweenExcludeRight"),
2346 Some(BetweenExcludeRight)
2347 );
2348 assert_eq!(
2349 WhereOperator::from_string("betweenExcludeRight"),
2350 Some(BetweenExcludeRight)
2351 );
2352 assert_eq!(
2353 WhereOperator::from_string("betweenexcluderight"),
2354 Some(BetweenExcludeRight)
2355 );
2356 assert_eq!(
2357 WhereOperator::from_string("between_exclude_right"),
2358 Some(BetweenExcludeRight)
2359 );
2360 }
2361
2362 #[test]
2363 fn from_string_parses_in_operator() {
2364 use super::WhereOperator;
2365 assert_eq!(WhereOperator::from_string("In"), Some(In));
2366 assert_eq!(WhereOperator::from_string("in"), Some(In));
2367 }
2368
2369 #[test]
2370 fn from_string_parses_starts_with_operator() {
2371 use super::WhereOperator;
2372 assert_eq!(
2373 WhereOperator::from_string("StartsWith"),
2374 Some(super::StartsWith)
2375 );
2376 assert_eq!(
2377 WhereOperator::from_string("startsWith"),
2378 Some(super::StartsWith)
2379 );
2380 assert_eq!(
2381 WhereOperator::from_string("startswith"),
2382 Some(super::StartsWith)
2383 );
2384 assert_eq!(
2385 WhereOperator::from_string("starts_with"),
2386 Some(super::StartsWith)
2387 );
2388 }
2389
2390 #[test]
2391 fn from_string_returns_none_for_unknown() {
2392 use super::WhereOperator;
2393 assert_eq!(WhereOperator::from_string("LIKE"), None);
2394 assert_eq!(WhereOperator::from_string("!="), None);
2395 assert_eq!(WhereOperator::from_string(""), None);
2396 }
2397
2398 #[test]
2401 fn from_sql_operator_maps_known_operators() {
2402 use super::WhereOperator;
2403 use sqlparser::ast::BinaryOperator;
2404 assert_eq!(
2405 WhereOperator::from_sql_operator(BinaryOperator::Eq),
2406 Some(Equal)
2407 );
2408 assert_eq!(
2409 WhereOperator::from_sql_operator(BinaryOperator::Gt),
2410 Some(GreaterThan)
2411 );
2412 assert_eq!(
2413 WhereOperator::from_sql_operator(BinaryOperator::GtEq),
2414 Some(GreaterThanOrEquals)
2415 );
2416 assert_eq!(
2417 WhereOperator::from_sql_operator(BinaryOperator::Lt),
2418 Some(LessThan)
2419 );
2420 assert_eq!(
2421 WhereOperator::from_sql_operator(BinaryOperator::LtEq),
2422 Some(LessThanOrEquals)
2423 );
2424 }
2425
2426 #[test]
2427 fn from_sql_operator_returns_none_for_unsupported() {
2428 use super::WhereOperator;
2429 use sqlparser::ast::BinaryOperator;
2430 assert_eq!(
2431 WhereOperator::from_sql_operator(BinaryOperator::NotEq),
2432 None
2433 );
2434 assert_eq!(WhereOperator::from_sql_operator(BinaryOperator::Plus), None);
2435 }
2436
2437 #[test]
2440 fn eval_equal_matches_identical_values() {
2441 assert!(Equal.eval(&Value::I64(42), &Value::I64(42)));
2442 assert!(!Equal.eval(&Value::I64(42), &Value::I64(43)));
2443 }
2444
2445 #[test]
2446 fn eval_greater_than() {
2447 assert!(GreaterThan.eval(&Value::I64(10), &Value::I64(5)));
2448 assert!(!GreaterThan.eval(&Value::I64(5), &Value::I64(10)));
2449 assert!(!GreaterThan.eval(&Value::I64(5), &Value::I64(5)));
2450 }
2451
2452 #[test]
2453 fn eval_greater_than_or_equals() {
2454 assert!(GreaterThanOrEquals.eval(&Value::I64(10), &Value::I64(5)));
2455 assert!(GreaterThanOrEquals.eval(&Value::I64(5), &Value::I64(5)));
2456 assert!(!GreaterThanOrEquals.eval(&Value::I64(4), &Value::I64(5)));
2457 }
2458
2459 #[test]
2460 fn eval_less_than() {
2461 assert!(LessThan.eval(&Value::I64(3), &Value::I64(5)));
2462 assert!(!LessThan.eval(&Value::I64(5), &Value::I64(3)));
2463 assert!(!LessThan.eval(&Value::I64(5), &Value::I64(5)));
2464 }
2465
2466 #[test]
2467 fn eval_less_than_or_equals() {
2468 assert!(LessThanOrEquals.eval(&Value::I64(3), &Value::I64(5)));
2469 assert!(LessThanOrEquals.eval(&Value::I64(5), &Value::I64(5)));
2470 assert!(!LessThanOrEquals.eval(&Value::I64(6), &Value::I64(5)));
2471 }
2472
2473 #[test]
2474 fn eval_in_with_array() {
2475 let arr = Value::Array(vec![Value::I64(1), Value::I64(2), Value::I64(3)]);
2476 assert!(In.eval(&Value::I64(2), &arr));
2477 assert!(!In.eval(&Value::I64(4), &arr));
2478 }
2479
2480 #[test]
2481 fn eval_in_with_bytes() {
2482 let bytes = Value::Bytes(vec![10, 20, 30]);
2483 assert!(In.eval(&Value::U8(20), &bytes));
2484 assert!(!In.eval(&Value::U8(40), &bytes));
2485 assert!(!In.eval(&Value::I64(20), &bytes));
2487 }
2488
2489 #[test]
2490 fn eval_in_with_non_collection_returns_false() {
2491 assert!(!In.eval(&Value::I64(1), &Value::I64(1)));
2492 }
2493
2494 #[test]
2495 fn eval_between_inclusive() {
2496 let bounds = Value::Array(vec![Value::I64(10), Value::I64(20)]);
2497 assert!(Between.eval(&Value::I64(10), &bounds));
2498 assert!(Between.eval(&Value::I64(15), &bounds));
2499 assert!(Between.eval(&Value::I64(20), &bounds));
2500 assert!(!Between.eval(&Value::I64(9), &bounds));
2501 assert!(!Between.eval(&Value::I64(21), &bounds));
2502 }
2503
2504 #[test]
2505 fn eval_between_exclude_bounds() {
2506 let bounds = Value::Array(vec![Value::I64(10), Value::I64(20)]);
2507 assert!(!BetweenExcludeBounds.eval(&Value::I64(10), &bounds));
2508 assert!(BetweenExcludeBounds.eval(&Value::I64(15), &bounds));
2509 assert!(!BetweenExcludeBounds.eval(&Value::I64(20), &bounds));
2510 }
2511
2512 #[test]
2513 fn eval_between_exclude_left() {
2514 let bounds = Value::Array(vec![Value::I64(10), Value::I64(20)]);
2515 assert!(!BetweenExcludeLeft.eval(&Value::I64(10), &bounds));
2516 assert!(BetweenExcludeLeft.eval(&Value::I64(15), &bounds));
2517 assert!(BetweenExcludeLeft.eval(&Value::I64(20), &bounds));
2518 }
2519
2520 #[test]
2521 fn eval_between_exclude_right() {
2522 let bounds = Value::Array(vec![Value::I64(10), Value::I64(20)]);
2523 assert!(BetweenExcludeRight.eval(&Value::I64(10), &bounds));
2524 assert!(BetweenExcludeRight.eval(&Value::I64(15), &bounds));
2525 assert!(!BetweenExcludeRight.eval(&Value::I64(20), &bounds));
2526 }
2527
2528 #[test]
2529 fn eval_between_with_wrong_bound_order_returns_false() {
2530 let bounds = Value::Array(vec![Value::I64(20), Value::I64(10)]);
2532 assert!(!Between.eval(&Value::I64(15), &bounds));
2533 assert!(!BetweenExcludeBounds.eval(&Value::I64(15), &bounds));
2534 assert!(!BetweenExcludeLeft.eval(&Value::I64(15), &bounds));
2535 assert!(!BetweenExcludeRight.eval(&Value::I64(15), &bounds));
2536 }
2537
2538 #[test]
2539 fn eval_between_with_non_array_returns_false() {
2540 assert!(!Between.eval(&Value::I64(5), &Value::I64(10)));
2541 }
2542
2543 #[test]
2544 fn eval_between_with_wrong_array_len_returns_false() {
2545 let single = Value::Array(vec![Value::I64(10)]);
2546 assert!(!Between.eval(&Value::I64(10), &single));
2547 }
2548
2549 #[test]
2550 fn eval_starts_with_text() {
2551 assert!(super::StartsWith.eval(
2552 &Value::Text("hello world".to_string()),
2553 &Value::Text("hello".to_string())
2554 ));
2555 assert!(!super::StartsWith.eval(
2556 &Value::Text("hello world".to_string()),
2557 &Value::Text("world".to_string())
2558 ));
2559 }
2560
2561 #[test]
2562 fn eval_starts_with_non_text_returns_false() {
2563 assert!(!super::StartsWith.eval(&Value::I64(123), &Value::Text("1".to_string())));
2564 assert!(!super::StartsWith.eval(&Value::Text("hello".to_string()), &Value::I64(1)));
2565 }
2566
2567 #[test]
2570 fn display_formatting_for_all_operators() {
2571 assert_eq!(format!("{}", Equal), "=");
2572 assert_eq!(format!("{}", GreaterThan), ">");
2573 assert_eq!(format!("{}", GreaterThanOrEquals), ">=");
2574 assert_eq!(format!("{}", LessThan), "<");
2575 assert_eq!(format!("{}", LessThanOrEquals), "<=");
2576 assert_eq!(format!("{}", Between), "Between");
2577 assert_eq!(format!("{}", BetweenExcludeBounds), "BetweenExcludeBounds");
2578 assert_eq!(format!("{}", BetweenExcludeLeft), "BetweenExcludeLeft");
2579 assert_eq!(format!("{}", BetweenExcludeRight), "BetweenExcludeRight");
2580 assert_eq!(format!("{}", In), "In");
2581 assert_eq!(format!("{}", super::StartsWith), "StartsWith");
2582 }
2583
2584 #[test]
2587 fn where_operator_into_value() {
2588 let val: Value = Equal.into();
2589 assert_eq!(val, Value::Text("=".to_string()));
2590
2591 let val: Value = In.into();
2592 assert_eq!(val, Value::Text("In".to_string()));
2593 }
2594
2595 #[test]
2598 fn is_identifier_returns_true_for_dollar_id() {
2599 let clause = WhereClause {
2600 field: "$id".to_string(),
2601 operator: Equal,
2602 value: Value::I64(1),
2603 };
2604 assert!(clause.is_identifier());
2605 }
2606
2607 #[test]
2608 fn is_identifier_returns_false_for_other_fields() {
2609 let clause = WhereClause {
2610 field: "name".to_string(),
2611 operator: Equal,
2612 value: Value::I64(1),
2613 };
2614 assert!(!clause.is_identifier());
2615
2616 let clause = WhereClause {
2617 field: "$ownerId".to_string(),
2618 operator: Equal,
2619 value: Value::I64(1),
2620 };
2621 assert!(!clause.is_identifier());
2622 }
2623
2624 #[test]
2627 fn in_values_with_array() {
2628 let clause = WhereClause {
2629 field: "f".to_string(),
2630 operator: In,
2631 value: Value::Array(vec![Value::I64(1), Value::I64(2)]),
2632 };
2633 let result = clause.in_values();
2634 assert!(result.is_valid());
2635 let data = result.into_data().expect("should have data");
2636 assert_eq!(data.len(), 2);
2637 }
2638
2639 #[test]
2640 fn in_values_with_bytes() {
2641 let clause = WhereClause {
2642 field: "f".to_string(),
2643 operator: In,
2644 value: Value::Bytes(vec![10, 20]),
2645 };
2646 let result = clause.in_values();
2647 assert!(result.is_valid());
2648 let data = result.into_data().expect("should have data");
2649 assert_eq!(data.len(), 2);
2650 assert_eq!(data[0], Value::U8(10));
2651 assert_eq!(data[1], Value::U8(20));
2652 }
2653
2654 #[test]
2655 fn in_values_non_array_returns_error() {
2656 let clause = WhereClause {
2657 field: "f".to_string(),
2658 operator: In,
2659 value: Value::I64(42),
2660 };
2661 let result = clause.in_values();
2662 assert!(!result.is_valid());
2663 }
2664
2665 #[test]
2666 fn in_values_empty_array_returns_error() {
2667 let clause = WhereClause {
2668 field: "f".to_string(),
2669 operator: In,
2670 value: Value::Array(vec![]),
2671 };
2672 let result = clause.in_values();
2673 assert!(!result.is_valid());
2674 }
2675
2676 #[test]
2677 fn in_values_too_many_returns_error() {
2678 let values: Vec<Value> = (0..101).map(Value::I64).collect();
2679 let clause = WhereClause {
2680 field: "f".to_string(),
2681 operator: In,
2682 value: Value::Array(values),
2683 };
2684 let result = clause.in_values();
2685 assert!(!result.is_valid());
2686 }
2687
2688 #[test]
2689 fn in_values_with_duplicates_returns_error() {
2690 let clause = WhereClause {
2691 field: "f".to_string(),
2692 operator: In,
2693 value: Value::Array(vec![Value::I64(1), Value::I64(1)]),
2694 };
2695 let result = clause.in_values();
2696 assert!(!result.is_valid());
2697 }
2698
2699 #[test]
2702 fn less_than_with_i128_values() {
2703 let a = WhereClause {
2704 field: "f".to_string(),
2705 operator: Equal,
2706 value: Value::I128(5),
2707 };
2708 let b = WhereClause {
2709 field: "f".to_string(),
2710 operator: Equal,
2711 value: Value::I128(10),
2712 };
2713 assert!(a.less_than(&b, false).unwrap());
2714 assert!(a.less_than(&b, true).unwrap());
2715 assert!(!b.less_than(&a, false).unwrap());
2716 assert!(a.less_than(&a, true).unwrap()); assert!(!a.less_than(&a, false).unwrap()); }
2719
2720 #[test]
2721 fn less_than_with_u128_values() {
2722 let a = WhereClause {
2723 field: "f".to_string(),
2724 operator: Equal,
2725 value: Value::U128(1),
2726 };
2727 let b = WhereClause {
2728 field: "f".to_string(),
2729 operator: Equal,
2730 value: Value::U128(2),
2731 };
2732 assert!(a.less_than(&b, false).unwrap());
2733 assert!(!b.less_than(&a, false).unwrap());
2734 }
2735
2736 #[test]
2737 fn less_than_with_i64_values() {
2738 let a = WhereClause {
2739 field: "f".to_string(),
2740 operator: Equal,
2741 value: Value::I64(-5),
2742 };
2743 let b = WhereClause {
2744 field: "f".to_string(),
2745 operator: Equal,
2746 value: Value::I64(10),
2747 };
2748 assert!(a.less_than(&b, false).unwrap());
2749 }
2750
2751 #[test]
2752 fn less_than_with_u64_values() {
2753 let a = WhereClause {
2754 field: "f".to_string(),
2755 operator: Equal,
2756 value: Value::U64(3),
2757 };
2758 let b = WhereClause {
2759 field: "f".to_string(),
2760 operator: Equal,
2761 value: Value::U64(7),
2762 };
2763 assert!(a.less_than(&b, false).unwrap());
2764 }
2765
2766 #[test]
2767 fn less_than_with_i32_values() {
2768 let a = WhereClause {
2769 field: "f".to_string(),
2770 operator: Equal,
2771 value: Value::I32(1),
2772 };
2773 let b = WhereClause {
2774 field: "f".to_string(),
2775 operator: Equal,
2776 value: Value::I32(2),
2777 };
2778 assert!(a.less_than(&b, false).unwrap());
2779 }
2780
2781 #[test]
2782 fn less_than_with_u32_values() {
2783 let a = WhereClause {
2784 field: "f".to_string(),
2785 operator: Equal,
2786 value: Value::U32(1),
2787 };
2788 let b = WhereClause {
2789 field: "f".to_string(),
2790 operator: Equal,
2791 value: Value::U32(2),
2792 };
2793 assert!(a.less_than(&b, false).unwrap());
2794 }
2795
2796 #[test]
2797 fn less_than_with_i16_values() {
2798 let a = WhereClause {
2799 field: "f".to_string(),
2800 operator: Equal,
2801 value: Value::I16(1),
2802 };
2803 let b = WhereClause {
2804 field: "f".to_string(),
2805 operator: Equal,
2806 value: Value::I16(2),
2807 };
2808 assert!(a.less_than(&b, false).unwrap());
2809 assert!(a.less_than(&b, true).unwrap());
2810 }
2811
2812 #[test]
2813 fn less_than_with_u16_values() {
2814 let a = WhereClause {
2815 field: "f".to_string(),
2816 operator: Equal,
2817 value: Value::U16(1),
2818 };
2819 let b = WhereClause {
2820 field: "f".to_string(),
2821 operator: Equal,
2822 value: Value::U16(2),
2823 };
2824 assert!(a.less_than(&b, false).unwrap());
2825 }
2826
2827 #[test]
2828 fn less_than_with_i8_values() {
2829 let a = WhereClause {
2830 field: "f".to_string(),
2831 operator: Equal,
2832 value: Value::I8(1),
2833 };
2834 let b = WhereClause {
2835 field: "f".to_string(),
2836 operator: Equal,
2837 value: Value::I8(2),
2838 };
2839 assert!(a.less_than(&b, false).unwrap());
2840 }
2841
2842 #[test]
2843 fn less_than_with_u8_values() {
2844 let a = WhereClause {
2845 field: "f".to_string(),
2846 operator: Equal,
2847 value: Value::U8(1),
2848 };
2849 let b = WhereClause {
2850 field: "f".to_string(),
2851 operator: Equal,
2852 value: Value::U8(2),
2853 };
2854 assert!(a.less_than(&b, false).unwrap());
2855 }
2856
2857 #[test]
2858 fn less_than_with_bytes_values() {
2859 let a = WhereClause {
2860 field: "f".to_string(),
2861 operator: Equal,
2862 value: Value::Bytes(vec![1, 2]),
2863 };
2864 let b = WhereClause {
2865 field: "f".to_string(),
2866 operator: Equal,
2867 value: Value::Bytes(vec![1, 3]),
2868 };
2869 assert!(a.less_than(&b, false).unwrap());
2870 }
2871
2872 #[test]
2873 fn less_than_with_float_values() {
2874 let a = WhereClause {
2875 field: "f".to_string(),
2876 operator: Equal,
2877 value: Value::Float(1.5),
2878 };
2879 let b = WhereClause {
2880 field: "f".to_string(),
2881 operator: Equal,
2882 value: Value::Float(2.5),
2883 };
2884 assert!(a.less_than(&b, false).unwrap());
2885 assert!(a.less_than(&b, true).unwrap());
2886 }
2887
2888 #[test]
2889 fn less_than_with_text_values() {
2890 let a = WhereClause {
2891 field: "f".to_string(),
2892 operator: Equal,
2893 value: Value::Text("abc".to_string()),
2894 };
2895 let b = WhereClause {
2896 field: "f".to_string(),
2897 operator: Equal,
2898 value: Value::Text("xyz".to_string()),
2899 };
2900 assert!(a.less_than(&b, false).unwrap());
2901 }
2902
2903 #[test]
2904 fn less_than_with_mismatched_types_returns_error() {
2905 let a = WhereClause {
2906 field: "f".to_string(),
2907 operator: Equal,
2908 value: Value::I64(1),
2909 };
2910 let b = WhereClause {
2911 field: "f".to_string(),
2912 operator: Equal,
2913 value: Value::Text("abc".to_string()),
2914 };
2915 assert!(a.less_than(&b, false).is_err());
2916 }
2917
2918 #[test]
2921 fn from_components_valid_clause() {
2922 let components = vec![
2923 Value::Text("name".to_string()),
2924 Value::Text("=".to_string()),
2925 Value::Text("alice".to_string()),
2926 ];
2927 let clause = WhereClause::from_components(&components).unwrap();
2928 assert_eq!(clause.field, "name");
2929 assert_eq!(clause.operator, Equal);
2930 assert_eq!(clause.value, Value::Text("alice".to_string()));
2931 }
2932
2933 #[test]
2934 fn from_components_wrong_count_returns_error() {
2935 let components = vec![
2936 Value::Text("name".to_string()),
2937 Value::Text("=".to_string()),
2938 ];
2939 assert!(WhereClause::from_components(&components).is_err());
2940
2941 let components = vec![
2942 Value::Text("name".to_string()),
2943 Value::Text("=".to_string()),
2944 Value::I64(1),
2945 Value::I64(2),
2946 ];
2947 assert!(WhereClause::from_components(&components).is_err());
2948 }
2949
2950 #[test]
2951 fn from_components_non_string_field_returns_error() {
2952 let components = vec![Value::I64(123), Value::Text("=".to_string()), Value::I64(1)];
2953 assert!(WhereClause::from_components(&components).is_err());
2954 }
2955
2956 #[test]
2957 fn from_components_non_string_operator_returns_error() {
2958 let components = vec![
2959 Value::Text("name".to_string()),
2960 Value::I64(1),
2961 Value::I64(1),
2962 ];
2963 assert!(WhereClause::from_components(&components).is_err());
2964 }
2965
2966 #[test]
2967 fn from_components_unknown_operator_returns_error() {
2968 let components = vec![
2969 Value::Text("name".to_string()),
2970 Value::Text("LIKE".to_string()),
2971 Value::I64(1),
2972 ];
2973 assert!(WhereClause::from_components(&components).is_err());
2974 }
2975
2976 #[test]
2977 fn from_components_with_in_operator() {
2978 let components = vec![
2979 Value::Text("status".to_string()),
2980 Value::Text("in".to_string()),
2981 Value::Array(vec![Value::I64(1), Value::I64(2)]),
2982 ];
2983 let clause = WhereClause::from_components(&components).unwrap();
2984 assert_eq!(clause.operator, In);
2985 }
2986
2987 #[test]
2988 fn from_components_with_starts_with_operator() {
2989 let components = vec![
2990 Value::Text("name".to_string()),
2991 Value::Text("startsWith".to_string()),
2992 Value::Text("alice".to_string()),
2993 ];
2994 let clause = WhereClause::from_components(&components).unwrap();
2995 assert_eq!(clause.operator, super::StartsWith);
2996 }
2997
2998 #[test]
3001 fn where_clause_into_value() {
3002 let clause = WhereClause {
3003 field: "name".to_string(),
3004 operator: Equal,
3005 value: Value::Text("alice".to_string()),
3006 };
3007 let val: Value = clause.into();
3008 match val {
3009 Value::Array(arr) => {
3010 assert_eq!(arr.len(), 3);
3011 assert_eq!(arr[0], Value::Text("name".to_string()));
3012 assert_eq!(arr[1], Value::Text("=".to_string()));
3013 assert_eq!(arr[2], Value::Text("alice".to_string()));
3014 }
3015 _ => panic!("expected Array"),
3016 }
3017 }
3018
3019 #[test]
3022 fn value_clause_matches_value_equal() {
3023 let clause = ValueClause {
3024 operator: Equal,
3025 value: Value::I64(42),
3026 };
3027 assert!(clause.matches_value(&Value::I64(42)));
3028 assert!(!clause.matches_value(&Value::I64(43)));
3029 }
3030
3031 #[test]
3032 fn value_clause_matches_value_greater_than() {
3033 let clause = ValueClause {
3034 operator: GreaterThan,
3035 value: Value::I64(10),
3036 };
3037 assert!(clause.matches_value(&Value::I64(20)));
3038 assert!(!clause.matches_value(&Value::I64(5)));
3039 }
3040
3041 #[test]
3042 fn value_clause_matches_value_in() {
3043 let clause = ValueClause {
3044 operator: In,
3045 value: Value::Array(vec![Value::I64(1), Value::I64(2), Value::I64(3)]),
3046 };
3047 assert!(clause.matches_value(&Value::I64(2)));
3048 assert!(!clause.matches_value(&Value::I64(4)));
3049 }
3050
3051 #[test]
3052 fn value_clause_matches_value_starts_with() {
3053 let clause = ValueClause {
3054 operator: super::StartsWith,
3055 value: Value::Text("hello".to_string()),
3056 };
3057 assert!(clause.matches_value(&Value::Text("hello world".to_string())));
3058 assert!(!clause.matches_value(&Value::Text("world hello".to_string())));
3059 }
3060
3061 #[test]
3064 fn where_clause_matches_value_delegates_to_eval() {
3065 let clause = WhereClause {
3066 field: "age".to_string(),
3067 operator: GreaterThanOrEquals,
3068 value: Value::I64(18),
3069 };
3070 assert!(clause.matches_value(&Value::I64(18)));
3071 assert!(clause.matches_value(&Value::I64(25)));
3072 assert!(!clause.matches_value(&Value::I64(17)));
3073 }
3074
3075 #[test]
3078 fn group_clauses_empty_input() {
3079 let clauses: Vec<WhereClause> = vec![];
3080 let (eq, range, in_c) = WhereClause::group_clauses(&clauses, PlatformVersion::latest())
3081 .expect("empty should succeed");
3082 assert!(eq.is_empty());
3083 assert!(range.is_none());
3084 assert!(in_c.is_empty());
3085 }
3086
3087 #[test]
3088 fn group_clauses_single_equality() {
3089 let clauses = vec![WhereClause {
3090 field: "name".to_string(),
3091 operator: Equal,
3092 value: Value::Text("alice".to_string()),
3093 }];
3094 let (eq, range, in_c) =
3095 WhereClause::group_clauses(&clauses, PlatformVersion::latest()).unwrap();
3096 assert_eq!(eq.len(), 1);
3097 assert!(eq.contains_key("name"));
3098 assert!(range.is_none());
3099 assert!(in_c.is_empty());
3100 }
3101
3102 #[test]
3103 fn group_clauses_equality_on_id_is_excluded_from_equals() {
3104 let clauses = vec![WhereClause {
3105 field: "$id".to_string(),
3106 operator: Equal,
3107 value: Value::I64(1),
3108 }];
3109 let (eq, range, in_c) =
3110 WhereClause::group_clauses(&clauses, PlatformVersion::latest()).unwrap();
3111 assert!(eq.is_empty());
3113 assert!(range.is_none());
3114 assert!(in_c.is_empty());
3115 }
3116
3117 #[test]
3118 fn group_clauses_in_on_id_is_excluded_from_in_clause() {
3119 let clauses = vec![WhereClause {
3120 field: "$id".to_string(),
3121 operator: In,
3122 value: Value::Array(vec![Value::I64(1), Value::I64(2)]),
3123 }];
3124 let (eq, range, in_c) =
3125 WhereClause::group_clauses(&clauses, PlatformVersion::latest()).unwrap();
3126 assert!(eq.is_empty());
3127 assert!(range.is_none());
3128 assert!(in_c.is_empty());
3129 }
3130
3131 #[test]
3132 fn group_clauses_single_in() {
3133 let clauses = vec![WhereClause {
3134 field: "status".to_string(),
3135 operator: In,
3136 value: Value::Array(vec![Value::I64(1), Value::I64(2)]),
3137 }];
3138 let (eq, range, in_c) =
3139 WhereClause::group_clauses(&clauses, PlatformVersion::latest()).unwrap();
3140 assert!(eq.is_empty());
3141 assert!(range.is_none());
3142 assert_eq!(in_c.len(), 1);
3143 assert_eq!(in_c[0].field, "status");
3144 }
3145
3146 #[test]
3147 fn group_clauses_multiple_in_on_distinct_fields_groups_structurally() {
3148 let clauses = vec![
3152 WhereClause {
3153 field: "a".to_string(),
3154 operator: In,
3155 value: Value::Array(vec![Value::I64(1)]),
3156 },
3157 WhereClause {
3158 field: "b".to_string(),
3159 operator: In,
3160 value: Value::Array(vec![Value::I64(2)]),
3161 },
3162 ];
3163 let (eq, range, in_c) =
3164 WhereClause::group_clauses(&clauses, PlatformVersion::latest()).unwrap();
3165 assert!(eq.is_empty());
3166 assert!(range.is_none());
3167 assert_eq!(in_c.len(), 2);
3168 assert_eq!(in_c[0].field, "a");
3169 assert_eq!(in_c[1].field, "b");
3170 }
3171
3172 #[test]
3173 fn group_clauses_multiple_in_v13_reports_multiple_in_before_any_other_check() {
3174 let platform_version_13 =
3179 PlatformVersion::get(13).expect("protocol version 13 should exist");
3180 let shapes: Vec<Vec<WhereClause>> = vec![
3181 vec![
3183 WhereClause {
3184 field: "a".to_string(),
3185 operator: In,
3186 value: Value::Array(vec![Value::I64(1)]),
3187 },
3188 WhereClause {
3189 field: "a".to_string(),
3190 operator: In,
3191 value: Value::Array(vec![Value::I64(2)]),
3192 },
3193 ],
3194 vec![
3196 WhereClause {
3197 field: "a".to_string(),
3198 operator: Equal,
3199 value: Value::I64(1),
3200 },
3201 WhereClause {
3202 field: "a".to_string(),
3203 operator: In,
3204 value: Value::Array(vec![Value::I64(2)]),
3205 },
3206 WhereClause {
3207 field: "b".to_string(),
3208 operator: In,
3209 value: Value::Array(vec![Value::I64(3)]),
3210 },
3211 ],
3212 vec![
3214 WhereClause {
3215 field: "a".to_string(),
3216 operator: In,
3217 value: Value::Array(vec![Value::I64(1)]),
3218 },
3219 WhereClause {
3220 field: "b".to_string(),
3221 operator: In,
3222 value: Value::Array(vec![Value::I64(2)]),
3223 },
3224 WhereClause {
3225 field: "c".to_string(),
3226 operator: GreaterThan,
3227 value: Value::I64(5),
3228 },
3229 WhereClause {
3230 field: "d".to_string(),
3231 operator: super::LessThan,
3232 value: Value::I64(3),
3233 },
3234 ],
3235 ];
3236 for clauses in shapes {
3237 let error = WhereClause::group_clauses(&clauses, platform_version_13)
3238 .expect_err("multi-in shapes must be rejected at protocol version 13");
3239 assert!(
3240 matches!(
3241 error,
3242 crate::error::Error::Query(QuerySyntaxError::MultipleInClauses(_))
3243 ),
3244 "expected MultipleInClauses, got {error:?}"
3245 );
3246 }
3247 }
3248
3249 #[test]
3250 fn group_clauses_multiple_in_on_same_field_returns_error() {
3251 let clauses = vec![
3252 WhereClause {
3253 field: "a".to_string(),
3254 operator: In,
3255 value: Value::Array(vec![Value::I64(1)]),
3256 },
3257 WhereClause {
3258 field: "a".to_string(),
3259 operator: In,
3260 value: Value::Array(vec![Value::I64(2)]),
3261 },
3262 ];
3263 assert!(WhereClause::group_clauses(&clauses, PlatformVersion::latest()).is_err());
3264 }
3265
3266 #[test]
3267 fn group_clauses_in_same_field_as_equality_returns_error() {
3268 let clauses = vec![
3269 WhereClause {
3270 field: "status".to_string(),
3271 operator: Equal,
3272 value: Value::I64(1),
3273 },
3274 WhereClause {
3275 field: "status".to_string(),
3276 operator: In,
3277 value: Value::Array(vec![Value::I64(2)]),
3278 },
3279 ];
3280 assert!(WhereClause::group_clauses(&clauses, PlatformVersion::latest()).is_err());
3281 }
3282
3283 #[test]
3284 fn group_clauses_duplicate_equality_same_field_returns_error() {
3285 let clauses = vec![
3286 WhereClause {
3287 field: "name".to_string(),
3288 operator: Equal,
3289 value: Value::Text("alice".to_string()),
3290 },
3291 WhereClause {
3292 field: "name".to_string(),
3293 operator: Equal,
3294 value: Value::Text("bob".to_string()),
3295 },
3296 ];
3297 assert!(WhereClause::group_clauses(&clauses, PlatformVersion::latest()).is_err());
3298 }
3299
3300 #[test]
3301 fn group_clauses_single_range_operator() {
3302 let clauses = vec![WhereClause {
3303 field: "age".to_string(),
3304 operator: GreaterThan,
3305 value: Value::I64(18),
3306 }];
3307 let (eq, range, in_c) =
3308 WhereClause::group_clauses(&clauses, PlatformVersion::latest()).unwrap();
3309 assert!(eq.is_empty());
3310 assert!(range.is_some());
3311 assert_eq!(range.unwrap().operator, GreaterThan);
3312 assert!(in_c.is_empty());
3313 }
3314
3315 #[test]
3316 fn group_clauses_single_non_groupable_range_between() {
3317 let clauses = vec![WhereClause {
3318 field: "age".to_string(),
3319 operator: Between,
3320 value: Value::Array(vec![Value::Float(0.0), Value::Float(100.0)]),
3321 }];
3322 let (eq, range, in_c) =
3323 WhereClause::group_clauses(&clauses, PlatformVersion::latest()).unwrap();
3324 assert!(eq.is_empty());
3325 assert!(range.is_some());
3326 assert_eq!(range.unwrap().operator, Between);
3327 assert!(in_c.is_empty());
3328 }
3329
3330 #[test]
3331 fn group_clauses_starts_with_empty_string_returns_error() {
3332 let clauses = vec![WhereClause {
3333 field: "name".to_string(),
3334 operator: super::StartsWith,
3335 value: Value::Text("".to_string()),
3336 }];
3337 assert!(WhereClause::group_clauses(&clauses, PlatformVersion::latest()).is_err());
3338 }
3339
3340 #[test]
3341 fn group_clauses_starts_with_valid_string() {
3342 let clauses = vec![WhereClause {
3343 field: "name".to_string(),
3344 operator: super::StartsWith,
3345 value: Value::Text("al".to_string()),
3346 }];
3347 let (eq, range, in_c) =
3348 WhereClause::group_clauses(&clauses, PlatformVersion::latest()).unwrap();
3349 assert!(eq.is_empty());
3350 assert!(range.is_some());
3351 assert_eq!(range.unwrap().operator, super::StartsWith);
3352 assert!(in_c.is_empty());
3353 }
3354
3355 #[test]
3356 fn group_clauses_non_groupable_range_same_field_as_equality_returns_error() {
3357 let clauses = vec![
3358 WhereClause {
3359 field: "name".to_string(),
3360 operator: Equal,
3361 value: Value::Text("alice".to_string()),
3362 },
3363 WhereClause {
3364 field: "name".to_string(),
3365 operator: super::StartsWith,
3366 value: Value::Text("al".to_string()),
3367 },
3368 ];
3369 assert!(WhereClause::group_clauses(&clauses, PlatformVersion::latest()).is_err());
3370 }
3371
3372 #[test]
3373 fn group_clauses_multiple_non_groupable_ranges_returns_error() {
3374 let clauses = vec![
3375 WhereClause {
3376 field: "a".to_string(),
3377 operator: Between,
3378 value: Value::Array(vec![Value::Float(0.0), Value::Float(10.0)]),
3379 },
3380 WhereClause {
3381 field: "b".to_string(),
3382 operator: super::StartsWith,
3383 value: Value::Text("x".to_string()),
3384 },
3385 ];
3386 assert!(WhereClause::group_clauses(&clauses, PlatformVersion::latest()).is_err());
3387 }
3388
3389 #[test]
3390 fn group_clauses_mixed_groupable_and_non_groupable_returns_error() {
3391 let clauses = vec![
3392 WhereClause {
3393 field: "a".to_string(),
3394 operator: GreaterThan,
3395 value: Value::Float(0.0),
3396 },
3397 WhereClause {
3398 field: "b".to_string(),
3399 operator: Between,
3400 value: Value::Array(vec![Value::Float(0.0), Value::Float(10.0)]),
3401 },
3402 ];
3403 assert!(WhereClause::group_clauses(&clauses, PlatformVersion::latest()).is_err());
3404 }
3405
3406 #[test]
3407 fn group_clauses_three_groupable_ranges_returns_error() {
3408 let clauses = vec![
3409 WhereClause {
3410 field: "a".to_string(),
3411 operator: GreaterThan,
3412 value: Value::Float(0.0),
3413 },
3414 WhereClause {
3415 field: "a".to_string(),
3416 operator: LessThan,
3417 value: Value::Float(10.0),
3418 },
3419 WhereClause {
3420 field: "a".to_string(),
3421 operator: GreaterThanOrEquals,
3422 value: Value::Float(5.0),
3423 },
3424 ];
3425 assert!(WhereClause::group_clauses(&clauses, PlatformVersion::latest()).is_err());
3426 }
3427
3428 #[test]
3429 fn group_clauses_range_same_field_as_equality_returns_error() {
3430 let clauses = vec![
3431 WhereClause {
3432 field: "age".to_string(),
3433 operator: Equal,
3434 value: Value::I64(25),
3435 },
3436 WhereClause {
3437 field: "age".to_string(),
3438 operator: GreaterThan,
3439 value: Value::I64(18),
3440 },
3441 ];
3442 assert!(WhereClause::group_clauses(&clauses, PlatformVersion::latest()).is_err());
3443 }
3444
3445 #[test]
3446 fn group_clauses_two_ranges_combined_into_between() {
3447 let clauses = vec![
3448 WhereClause {
3449 field: "age".to_string(),
3450 operator: GreaterThanOrEquals,
3451 value: Value::Float(10.0),
3452 },
3453 WhereClause {
3454 field: "age".to_string(),
3455 operator: LessThanOrEquals,
3456 value: Value::Float(20.0),
3457 },
3458 ];
3459 let (_, range, _) =
3460 WhereClause::group_clauses(&clauses, PlatformVersion::latest()).unwrap();
3461 let r = range.unwrap();
3462 assert_eq!(r.operator, Between);
3463 assert_eq!(r.field, "age");
3464 }
3465
3466 #[test]
3467 fn group_clauses_two_ranges_combined_into_between_exclude_right() {
3468 let clauses = vec![
3469 WhereClause {
3470 field: "age".to_string(),
3471 operator: GreaterThanOrEquals,
3472 value: Value::Float(10.0),
3473 },
3474 WhereClause {
3475 field: "age".to_string(),
3476 operator: LessThan,
3477 value: Value::Float(20.0),
3478 },
3479 ];
3480 let (_, range, _) =
3481 WhereClause::group_clauses(&clauses, PlatformVersion::latest()).unwrap();
3482 assert_eq!(range.unwrap().operator, BetweenExcludeRight);
3483 }
3484
3485 #[test]
3486 fn group_clauses_two_ranges_combined_into_between_exclude_left() {
3487 let clauses = vec![
3488 WhereClause {
3489 field: "age".to_string(),
3490 operator: GreaterThan,
3491 value: Value::Float(10.0),
3492 },
3493 WhereClause {
3494 field: "age".to_string(),
3495 operator: LessThanOrEquals,
3496 value: Value::Float(20.0),
3497 },
3498 ];
3499 let (_, range, _) =
3500 WhereClause::group_clauses(&clauses, PlatformVersion::latest()).unwrap();
3501 assert_eq!(range.unwrap().operator, BetweenExcludeLeft);
3502 }
3503
3504 #[test]
3505 fn group_clauses_two_ranges_combined_into_between_exclude_bounds() {
3506 let clauses = vec![
3507 WhereClause {
3508 field: "age".to_string(),
3509 operator: GreaterThan,
3510 value: Value::Float(10.0),
3511 },
3512 WhereClause {
3513 field: "age".to_string(),
3514 operator: LessThan,
3515 value: Value::Float(20.0),
3516 },
3517 ];
3518 let (_, range, _) =
3519 WhereClause::group_clauses(&clauses, PlatformVersion::latest()).unwrap();
3520 assert_eq!(range.unwrap().operator, BetweenExcludeBounds);
3521 }
3522
3523 #[test]
3524 fn group_clauses_equality_plus_in_on_different_fields() {
3525 let clauses = vec![
3526 WhereClause {
3527 field: "name".to_string(),
3528 operator: Equal,
3529 value: Value::Text("alice".to_string()),
3530 },
3531 WhereClause {
3532 field: "status".to_string(),
3533 operator: In,
3534 value: Value::Array(vec![Value::I64(1), Value::I64(2)]),
3535 },
3536 ];
3537 let (eq, _, in_c) =
3538 WhereClause::group_clauses(&clauses, PlatformVersion::latest()).unwrap();
3539 assert_eq!(eq.len(), 1);
3540 assert_eq!(in_c.len(), 1);
3541 }
3542
3543 #[test]
3544 fn group_clauses_equality_plus_range_on_different_fields() {
3545 let clauses = vec![
3546 WhereClause {
3547 field: "name".to_string(),
3548 operator: Equal,
3549 value: Value::Text("alice".to_string()),
3550 },
3551 WhereClause {
3552 field: "age".to_string(),
3553 operator: GreaterThan,
3554 value: Value::Float(18.0),
3555 },
3556 ];
3557 let (eq, range, in_c) =
3558 WhereClause::group_clauses(&clauses, PlatformVersion::latest()).unwrap();
3559 assert_eq!(eq.len(), 1);
3560 assert!(range.is_some());
3561 assert!(in_c.is_empty());
3562 }
3563
3564 #[test]
3567 fn meta_field_property_type_all_identifiers() {
3568 use super::meta_field_property_type;
3569 use dpp::data_contract::document_type::DocumentPropertyType;
3570
3571 for field in ["$id", "$ownerId", "$dataContractId", "$creatorId"] {
3572 let pt = meta_field_property_type(field);
3573 assert!(
3574 matches!(
3575 pt,
3576 Some(
3577 DocumentPropertyType::Identifier
3578 | DocumentPropertyType::IdentifierWithReference(_)
3579 )
3580 ),
3581 "expected Identifier for {field}"
3582 );
3583 }
3584 }
3585
3586 #[test]
3587 fn meta_field_property_type_dates() {
3588 use super::meta_field_property_type;
3589 use dpp::data_contract::document_type::DocumentPropertyType;
3590
3591 for field in ["$createdAt", "$updatedAt", "$transferredAt"] {
3592 let pt = meta_field_property_type(field);
3593 assert!(
3594 matches!(pt, Some(DocumentPropertyType::Date)),
3595 "expected Date for {field}"
3596 );
3597 }
3598 }
3599
3600 #[test]
3601 fn meta_field_property_type_block_heights() {
3602 use super::meta_field_property_type;
3603 use dpp::data_contract::document_type::DocumentPropertyType;
3604
3605 for field in [
3606 "$createdAtBlockHeight",
3607 "$updatedAtBlockHeight",
3608 "$transferredAtBlockHeight",
3609 ] {
3610 let pt = meta_field_property_type(field);
3611 assert!(
3612 matches!(pt, Some(DocumentPropertyType::U64)),
3613 "expected U64 for {field}"
3614 );
3615 }
3616 }
3617
3618 #[test]
3619 fn meta_field_property_type_core_block_heights() {
3620 use super::meta_field_property_type;
3621 use dpp::data_contract::document_type::DocumentPropertyType;
3622
3623 for field in [
3624 "$createdAtCoreBlockHeight",
3625 "$updatedAtCoreBlockHeight",
3626 "$transferredAtCoreBlockHeight",
3627 ] {
3628 let pt = meta_field_property_type(field);
3629 assert!(
3630 matches!(pt, Some(DocumentPropertyType::U32)),
3631 "expected U32 for {field}"
3632 );
3633 }
3634 }
3635
3636 #[test]
3637 fn meta_field_property_type_revision_and_protocol_version() {
3638 use super::meta_field_property_type;
3639 use dpp::data_contract::document_type::DocumentPropertyType;
3640
3641 assert!(matches!(
3642 meta_field_property_type("$revision"),
3643 Some(DocumentPropertyType::U64)
3644 ));
3645 assert!(matches!(
3646 meta_field_property_type("$protocolVersion"),
3647 Some(DocumentPropertyType::U64)
3648 ));
3649 }
3650
3651 #[test]
3652 fn meta_field_property_type_type_field() {
3653 use super::meta_field_property_type;
3654 use dpp::data_contract::document_type::DocumentPropertyType;
3655
3656 assert!(matches!(
3657 meta_field_property_type("$type"),
3658 Some(DocumentPropertyType::String(_))
3659 ));
3660 }
3661
3662 #[test]
3663 fn meta_field_property_type_unknown_returns_none() {
3664 use super::meta_field_property_type;
3665
3666 assert!(meta_field_property_type("unknown").is_none());
3667 assert!(meta_field_property_type("$nonexistent").is_none());
3668 }
3669
3670 #[test]
3673 fn allowed_ops_for_numeric_types_include_ranges() {
3674 use super::allowed_ops_for_type;
3675 use dpp::data_contract::document_type::DocumentPropertyType;
3676
3677 for ty in [
3678 DocumentPropertyType::U8,
3679 DocumentPropertyType::I8,
3680 DocumentPropertyType::U16,
3681 DocumentPropertyType::I16,
3682 DocumentPropertyType::U32,
3683 DocumentPropertyType::I32,
3684 DocumentPropertyType::U64,
3685 DocumentPropertyType::I64,
3686 DocumentPropertyType::U128,
3687 DocumentPropertyType::I128,
3688 DocumentPropertyType::F64,
3689 DocumentPropertyType::Date,
3690 ] {
3691 let ops = allowed_ops_for_type(&ty);
3692 assert!(ops.contains(&Equal), "numeric type should allow Equal");
3693 assert!(ops.contains(&In), "numeric type should allow In");
3694 assert!(
3695 ops.contains(&GreaterThan),
3696 "numeric type should allow GreaterThan"
3697 );
3698 assert!(ops.contains(&Between), "numeric type should allow Between");
3699 assert!(
3700 !ops.contains(&super::StartsWith),
3701 "numeric type should not allow StartsWith"
3702 );
3703 }
3704 }
3705
3706 #[test]
3707 fn allowed_ops_for_string_includes_starts_with() {
3708 use super::allowed_ops_for_type;
3709 use dpp::data_contract::document_type::{DocumentPropertyType, StringPropertySizes};
3710
3711 let ty = DocumentPropertyType::String(StringPropertySizes {
3712 min_length: None,
3713 max_length: None,
3714 max_bytes: None,
3715 });
3716 let ops = allowed_ops_for_type(&ty);
3717 assert!(ops.contains(&super::StartsWith));
3718 assert!(ops.contains(&Equal));
3719 assert!(ops.contains(&In));
3720 assert!(ops.contains(&GreaterThan));
3721 }
3722
3723 #[test]
3724 fn allowed_ops_for_identifier_only_equal_and_in() {
3725 use super::allowed_ops_for_type;
3726 use dpp::data_contract::document_type::DocumentPropertyType;
3727
3728 let ops = allowed_ops_for_type(&DocumentPropertyType::Identifier);
3729 assert_eq!(ops, &[Equal, In]);
3730 }
3731
3732 #[test]
3733 fn allowed_ops_for_boolean_only_equal() {
3734 use super::allowed_ops_for_type;
3735 use dpp::data_contract::document_type::DocumentPropertyType;
3736
3737 let ops = allowed_ops_for_type(&DocumentPropertyType::Boolean);
3738 assert_eq!(ops, &[Equal]);
3739 }
3740
3741 #[test]
3742 fn allowed_ops_for_object_is_empty() {
3743 use super::allowed_ops_for_type;
3744 use dpp::data_contract::document_type::DocumentPropertyType;
3745
3746 let ops = allowed_ops_for_type(&DocumentPropertyType::Object(Default::default()));
3747 assert!(ops.is_empty());
3748 }
3749
3750 #[test]
3753 fn value_shape_ok_equal_always_true() {
3754 use super::WhereOperator;
3755 use dpp::data_contract::document_type::DocumentPropertyType;
3756
3757 assert!(WhereOperator::Equal.value_shape_ok(&Value::I64(1), &DocumentPropertyType::U64));
3759 assert!(WhereOperator::Equal
3760 .value_shape_ok(&Value::Text("x".into()), &DocumentPropertyType::Boolean));
3761 }
3762
3763 #[test]
3764 fn value_shape_ok_in_requires_array_or_bytes() {
3765 use super::WhereOperator;
3766 use dpp::data_contract::document_type::DocumentPropertyType;
3767
3768 assert!(WhereOperator::In.value_shape_ok(
3769 &Value::Array(vec![Value::I64(1)]),
3770 &DocumentPropertyType::U64
3771 ));
3772 assert!(WhereOperator::In.value_shape_ok(&Value::Bytes(vec![1]), &DocumentPropertyType::U8));
3773 assert!(!WhereOperator::In.value_shape_ok(&Value::I64(1), &DocumentPropertyType::U64));
3774 }
3775
3776 #[test]
3777 fn value_shape_ok_starts_with_requires_text() {
3778 use super::WhereOperator;
3779 use dpp::data_contract::document_type::{DocumentPropertyType, StringPropertySizes};
3780
3781 let str_ty = DocumentPropertyType::String(StringPropertySizes {
3782 min_length: None,
3783 max_length: None,
3784 max_bytes: None,
3785 });
3786 assert!(WhereOperator::StartsWith.value_shape_ok(&Value::Text("abc".into()), &str_ty));
3787 assert!(!WhereOperator::StartsWith.value_shape_ok(&Value::I64(1), &str_ty));
3788 }
3789
3790 #[test]
3791 fn value_shape_ok_range_for_f64_requires_numeric() {
3792 use super::WhereOperator;
3793 use dpp::data_contract::document_type::DocumentPropertyType;
3794
3795 assert!(WhereOperator::GreaterThan
3796 .value_shape_ok(&Value::Float(1.0), &DocumentPropertyType::F64));
3797 assert!(
3798 WhereOperator::GreaterThan.value_shape_ok(&Value::I64(1), &DocumentPropertyType::F64)
3799 );
3800 assert!(!WhereOperator::GreaterThan
3801 .value_shape_ok(&Value::Text("x".into()), &DocumentPropertyType::F64));
3802 }
3803
3804 #[test]
3805 fn value_shape_ok_range_for_string_requires_text() {
3806 use super::WhereOperator;
3807 use dpp::data_contract::document_type::{DocumentPropertyType, StringPropertySizes};
3808
3809 let str_ty = DocumentPropertyType::String(StringPropertySizes {
3810 min_length: None,
3811 max_length: None,
3812 max_bytes: None,
3813 });
3814 assert!(WhereOperator::LessThan.value_shape_ok(&Value::Text("a".into()), &str_ty));
3815 assert!(!WhereOperator::LessThan.value_shape_ok(&Value::I64(1), &str_ty));
3816 }
3817
3818 #[test]
3819 fn value_shape_ok_range_for_integer_requires_integer() {
3820 use super::WhereOperator;
3821 use dpp::data_contract::document_type::DocumentPropertyType;
3822
3823 assert!(
3824 WhereOperator::GreaterThan.value_shape_ok(&Value::U64(1), &DocumentPropertyType::U64)
3825 );
3826 assert!(
3827 WhereOperator::GreaterThan.value_shape_ok(&Value::I32(1), &DocumentPropertyType::I32)
3828 );
3829 assert!(!WhereOperator::GreaterThan
3830 .value_shape_ok(&Value::Float(1.0), &DocumentPropertyType::U64));
3831 assert!(!WhereOperator::GreaterThan
3832 .value_shape_ok(&Value::Text("x".into()), &DocumentPropertyType::U64));
3833 }
3834
3835 #[test]
3836 fn value_shape_ok_between_requires_array_of_two() {
3837 use super::WhereOperator;
3838 use dpp::data_contract::document_type::DocumentPropertyType;
3839
3840 let good = Value::Array(vec![Value::I64(1), Value::I64(10)]);
3841 assert!(WhereOperator::Between.value_shape_ok(&good, &DocumentPropertyType::I64));
3842
3843 let bad_len = Value::Array(vec![Value::I64(1)]);
3844 assert!(!WhereOperator::Between.value_shape_ok(&bad_len, &DocumentPropertyType::I64));
3845
3846 let not_array = Value::I64(5);
3847 assert!(!WhereOperator::Between.value_shape_ok(¬_array, &DocumentPropertyType::I64));
3848
3849 assert!(
3851 WhereOperator::BetweenExcludeBounds.value_shape_ok(&good, &DocumentPropertyType::I64)
3852 );
3853 assert!(WhereOperator::BetweenExcludeLeft.value_shape_ok(&good, &DocumentPropertyType::I64));
3854 assert!(
3855 WhereOperator::BetweenExcludeRight.value_shape_ok(&good, &DocumentPropertyType::I64)
3856 );
3857 }
3858
3859 #[test]
3862 fn validate_rejects_unknown_field() {
3863 let fixture = get_data_contract_fixture(None, 0, LATEST_PLATFORM_VERSION.protocol_version);
3864 let contract = fixture.data_contract_owned();
3865 let doc_type = contract
3866 .document_type_for_name("niceDocument")
3867 .expect("doc type exists");
3868
3869 let clause = WhereClause {
3870 field: "nonexistentField".to_string(),
3871 operator: Equal,
3872 value: Value::I64(1),
3873 };
3874 let res = clause.validate_against_schema(doc_type);
3875 assert!(res.is_err());
3876 }
3877
3878 #[test]
3879 fn validate_rejects_disallowed_operator_for_boolean() {
3880 let fixture = get_data_contract_fixture(None, 0, LATEST_PLATFORM_VERSION.protocol_version);
3881 let contract = fixture.data_contract_owned();
3882 let doc_type = contract
3883 .document_type_for_name("niceDocument")
3884 .expect("doc type exists");
3885
3886 let clause = WhereClause {
3890 field: "$type".to_string(),
3891 operator: super::StartsWith,
3892 value: Value::Text("nice".to_string()),
3893 };
3894 let res = clause.validate_against_schema(doc_type);
3895 assert!(res.is_valid());
3896 }
3897
3898 #[test]
3899 fn validate_rejects_starts_with_empty_string() {
3900 let fixture = get_data_contract_fixture(None, 0, LATEST_PLATFORM_VERSION.protocol_version);
3901 let contract = fixture.data_contract_owned();
3902 let doc_type = contract
3903 .document_type_for_name("niceDocument")
3904 .expect("doc type exists");
3905
3906 let clause = WhereClause {
3907 field: "$type".to_string(),
3908 operator: super::StartsWith,
3909 value: Value::Text("".to_string()),
3910 };
3911 let res = clause.validate_against_schema(doc_type);
3912 assert!(res.is_err());
3913 assert!(matches!(
3914 res.first_error(),
3915 Some(QuerySyntaxError::StartsWithIllegalString(_))
3916 ));
3917 }
3918
3919 #[test]
3920 fn validate_rejects_in_with_empty_array() {
3921 let fixture = get_data_contract_fixture(None, 0, LATEST_PLATFORM_VERSION.protocol_version);
3922 let contract = fixture.data_contract_owned();
3923 let doc_type = contract
3924 .document_type_for_name("niceDocument")
3925 .expect("doc type exists");
3926
3927 let clause = WhereClause {
3928 field: "$ownerId".to_string(),
3929 operator: In,
3930 value: Value::Array(vec![]),
3931 };
3932 let res = clause.validate_against_schema(doc_type);
3933 assert!(res.is_err());
3934 }
3935
3936 #[test]
3937 fn validate_rejects_in_with_duplicates() {
3938 let fixture = get_data_contract_fixture(None, 0, LATEST_PLATFORM_VERSION.protocol_version);
3939 let contract = fixture.data_contract_owned();
3940 let doc_type = contract
3941 .document_type_for_name("niceDocument")
3942 .expect("doc type exists");
3943
3944 let clause = WhereClause {
3945 field: "$ownerId".to_string(),
3946 operator: In,
3947 value: Value::Array(vec![
3948 Value::Identifier([1u8; 32]),
3949 Value::Identifier([1u8; 32]),
3950 ]),
3951 };
3952 let res = clause.validate_against_schema(doc_type);
3953 assert!(res.is_err());
3954 }
3955
3956 #[test]
3957 fn validate_rejects_between_with_descending_bounds() {
3958 let fixture = get_data_contract_fixture(None, 0, LATEST_PLATFORM_VERSION.protocol_version);
3959 let contract = fixture.data_contract_owned();
3960 let doc_type = contract
3961 .document_type_for_name("uniqueDates")
3962 .expect("doc type exists");
3963
3964 let clause = WhereClause {
3965 field: "$createdAt".to_string(),
3966 operator: Between,
3967 value: Value::Array(vec![Value::U64(2000), Value::U64(1000)]),
3968 };
3969 let res = clause.validate_against_schema(doc_type);
3970 assert!(res.is_err());
3971 assert!(matches!(
3972 res.first_error(),
3973 Some(QuerySyntaxError::InvalidBetweenClause(_))
3974 ));
3975 }
3976
3977 #[test]
3978 fn validate_rejects_range_operator_not_allowed_for_identifier() {
3979 let fixture = get_data_contract_fixture(None, 0, LATEST_PLATFORM_VERSION.protocol_version);
3980 let contract = fixture.data_contract_owned();
3981 let doc_type = contract
3982 .document_type_for_name("niceDocument")
3983 .expect("doc type exists");
3984
3985 let clause = WhereClause {
3986 field: "$ownerId".to_string(),
3987 operator: GreaterThan,
3988 value: Value::Identifier([1u8; 32]),
3989 };
3990 let res = clause.validate_against_schema(doc_type);
3991 assert!(res.is_err());
3992 }
3993
3994 #[test]
3995 fn validate_accepts_valid_integer_equality() {
3996 let fixture = get_data_contract_fixture(None, 0, LATEST_PLATFORM_VERSION.protocol_version);
3997 let contract = fixture.data_contract_owned();
3998 let doc_type = contract
3999 .document_type_for_name("niceDocument")
4000 .expect("doc type exists");
4001
4002 let clause = WhereClause {
4003 field: "$revision".to_string(),
4004 operator: Equal,
4005 value: Value::U64(5),
4006 };
4007 let res = clause.validate_against_schema(doc_type);
4008 assert!(res.is_valid());
4009 }
4010
4011 #[test]
4014 fn sql_value_boolean_true() {
4015 use super::sql_value_to_platform_value;
4016 let result = sql_value_to_platform_value(sqlparser::ast::Value::Boolean(true));
4017 assert_eq!(result, Some(Value::Bool(true)));
4018 }
4019
4020 #[test]
4021 fn sql_value_boolean_false() {
4022 use super::sql_value_to_platform_value;
4023 let result = sql_value_to_platform_value(sqlparser::ast::Value::Boolean(false));
4024 assert_eq!(result, Some(Value::Bool(false)));
4025 }
4026
4027 #[test]
4028 fn sql_value_number_integer() {
4029 use super::sql_value_to_platform_value;
4030 let result =
4031 sql_value_to_platform_value(sqlparser::ast::Value::Number("42".to_string(), false));
4032 assert_eq!(result, Some(Value::I64(42)));
4033 }
4034
4035 #[test]
4036 fn sql_value_number_negative_integer() {
4037 use super::sql_value_to_platform_value;
4038 let result =
4039 sql_value_to_platform_value(sqlparser::ast::Value::Number("-7".to_string(), false));
4040 assert_eq!(result, Some(Value::I64(-7)));
4041 }
4042
4043 #[test]
4044 fn sql_value_number_float() {
4045 use super::sql_value_to_platform_value;
4046 let result =
4047 sql_value_to_platform_value(sqlparser::ast::Value::Number("3.14".to_string(), false));
4048 assert_eq!(result, Some(Value::Float(3.14)));
4049 }
4050
4051 #[test]
4052 fn sql_value_number_unparseable_returns_none() {
4053 use super::sql_value_to_platform_value;
4054 let result = sql_value_to_platform_value(sqlparser::ast::Value::Number(
4056 "not_a_number".to_string(),
4057 false,
4058 ));
4059 assert_eq!(result, None);
4060 }
4061
4062 #[test]
4063 fn sql_value_single_quoted_string() {
4064 use super::sql_value_to_platform_value;
4065 let result = sql_value_to_platform_value(sqlparser::ast::Value::SingleQuotedString(
4066 "hello".to_string(),
4067 ));
4068 assert_eq!(result, Some(Value::Text("hello".to_string())));
4069 }
4070
4071 #[test]
4072 fn sql_value_double_quoted_string() {
4073 use super::sql_value_to_platform_value;
4074 let result = sql_value_to_platform_value(sqlparser::ast::Value::DoubleQuotedString(
4075 "world".to_string(),
4076 ));
4077 assert_eq!(result, Some(Value::Text("world".to_string())));
4078 }
4079
4080 #[test]
4081 fn sql_value_hex_string_literal() {
4082 use super::sql_value_to_platform_value;
4083 let result = sql_value_to_platform_value(sqlparser::ast::Value::HexStringLiteral(
4084 "0xABCD".to_string(),
4085 ));
4086 assert_eq!(result, Some(Value::Text("0xABCD".to_string())));
4087 }
4088
4089 #[test]
4090 fn sql_value_national_string_literal() {
4091 use super::sql_value_to_platform_value;
4092 let result = sql_value_to_platform_value(sqlparser::ast::Value::NationalStringLiteral(
4093 "n_str".to_string(),
4094 ));
4095 assert_eq!(result, Some(Value::Text("n_str".to_string())));
4096 }
4097
4098 #[test]
4099 fn sql_value_null_returns_none() {
4100 use super::sql_value_to_platform_value;
4101 let result = sql_value_to_platform_value(sqlparser::ast::Value::Null);
4102 assert_eq!(result, None);
4103 }
4104
4105 #[test]
4106 fn sql_value_placeholder_returns_none() {
4107 use super::sql_value_to_platform_value;
4108 let result =
4109 sql_value_to_platform_value(sqlparser::ast::Value::Placeholder("?".to_string()));
4110 assert_eq!(result, None);
4111 }
4112
4113 #[test]
4116 fn from_components_with_between_operator() {
4117 let components = vec![
4118 Value::Text("age".to_string()),
4119 Value::Text("between".to_string()),
4120 Value::Array(vec![Value::I64(10), Value::I64(20)]),
4121 ];
4122 let clause = WhereClause::from_components(&components).unwrap();
4123 assert_eq!(clause.field, "age");
4124 assert_eq!(clause.operator, Between);
4125 assert_eq!(
4126 clause.value,
4127 Value::Array(vec![Value::I64(10), Value::I64(20)])
4128 );
4129 }
4130
4131 #[test]
4132 fn from_components_with_between_exclude_bounds_operator() {
4133 let components = vec![
4134 Value::Text("score".to_string()),
4135 Value::Text("betweenExcludeBounds".to_string()),
4136 Value::Array(vec![Value::Float(1.0), Value::Float(9.0)]),
4137 ];
4138 let clause = WhereClause::from_components(&components).unwrap();
4139 assert_eq!(clause.operator, BetweenExcludeBounds);
4140 }
4141
4142 #[test]
4143 fn from_components_with_greater_than_or_equals() {
4144 let components = vec![
4145 Value::Text("price".to_string()),
4146 Value::Text(">=".to_string()),
4147 Value::U64(100),
4148 ];
4149 let clause = WhereClause::from_components(&components).unwrap();
4150 assert_eq!(clause.operator, GreaterThanOrEquals);
4151 assert_eq!(clause.value, Value::U64(100));
4152 }
4153
4154 #[test]
4155 fn from_components_with_less_than() {
4156 let components = vec![
4157 Value::Text("height".to_string()),
4158 Value::Text("<".to_string()),
4159 Value::I64(200),
4160 ];
4161 let clause = WhereClause::from_components(&components).unwrap();
4162 assert_eq!(clause.operator, LessThan);
4163 }
4164
4165 #[test]
4166 fn from_components_with_less_than_or_equals() {
4167 let components = vec![
4168 Value::Text("height".to_string()),
4169 Value::Text("<=".to_string()),
4170 Value::I64(200),
4171 ];
4172 let clause = WhereClause::from_components(&components).unwrap();
4173 assert_eq!(clause.operator, LessThanOrEquals);
4174 }
4175
4176 #[test]
4177 fn from_components_preserves_value_type() {
4178 let components = vec![
4180 Value::Text("tags".to_string()),
4181 Value::Text("in".to_string()),
4182 Value::Array(vec![
4183 Value::Text("a".to_string()),
4184 Value::Text("b".to_string()),
4185 Value::Text("c".to_string()),
4186 ]),
4187 ];
4188 let clause = WhereClause::from_components(&components).unwrap();
4189 assert_eq!(clause.operator, In);
4190 if let Value::Array(arr) = &clause.value {
4191 assert_eq!(arr.len(), 3);
4192 } else {
4193 panic!("expected Array value");
4194 }
4195 }
4196
4197 #[test]
4198 fn from_components_empty_returns_error() {
4199 let components: Vec<Value> = vec![];
4200 assert!(WhereClause::from_components(&components).is_err());
4201 }
4202
4203 #[test]
4204 fn from_components_single_element_returns_error() {
4205 let components = vec![Value::Text("name".to_string())];
4206 assert!(WhereClause::from_components(&components).is_err());
4207 }
4208
4209 #[test]
4212 fn less_than_u64_equal_values_with_allow_eq() {
4213 let a = WhereClause {
4214 field: "f".to_string(),
4215 operator: Equal,
4216 value: Value::U64(10),
4217 };
4218 assert!(a.less_than(&a, true).unwrap()); assert!(!a.less_than(&a, false).unwrap()); }
4221
4222 #[test]
4223 fn less_than_u32_equal_values_with_allow_eq() {
4224 let a = WhereClause {
4225 field: "f".to_string(),
4226 operator: Equal,
4227 value: Value::U32(5),
4228 };
4229 assert!(a.less_than(&a, true).unwrap());
4230 assert!(!a.less_than(&a, false).unwrap());
4231 }
4232
4233 #[test]
4234 fn less_than_i32_equal_values_with_allow_eq() {
4235 let a = WhereClause {
4236 field: "f".to_string(),
4237 operator: Equal,
4238 value: Value::I32(-3),
4239 };
4240 assert!(a.less_than(&a, true).unwrap());
4241 assert!(!a.less_than(&a, false).unwrap());
4242 }
4243
4244 #[test]
4245 fn less_than_u16_equal_values_with_allow_eq() {
4246 let a = WhereClause {
4247 field: "f".to_string(),
4248 operator: Equal,
4249 value: Value::U16(100),
4250 };
4251 assert!(a.less_than(&a, true).unwrap());
4252 assert!(!a.less_than(&a, false).unwrap());
4253 }
4254
4255 #[test]
4256 fn less_than_u8_equal_values_with_allow_eq() {
4257 let a = WhereClause {
4258 field: "f".to_string(),
4259 operator: Equal,
4260 value: Value::U8(7),
4261 };
4262 assert!(a.less_than(&a, true).unwrap());
4263 assert!(!a.less_than(&a, false).unwrap());
4264 }
4265
4266 #[test]
4267 fn less_than_i8_equal_values_with_allow_eq() {
4268 let a = WhereClause {
4269 field: "f".to_string(),
4270 operator: Equal,
4271 value: Value::I8(-1),
4272 };
4273 assert!(a.less_than(&a, true).unwrap());
4274 assert!(!a.less_than(&a, false).unwrap());
4275 }
4276
4277 #[test]
4278 fn less_than_u128_equal_values_with_allow_eq() {
4279 let a = WhereClause {
4280 field: "f".to_string(),
4281 operator: Equal,
4282 value: Value::U128(999),
4283 };
4284 assert!(a.less_than(&a, true).unwrap());
4285 assert!(!a.less_than(&a, false).unwrap());
4286 }
4287
4288 #[test]
4289 fn less_than_bytes_equal_values_with_allow_eq() {
4290 let a = WhereClause {
4291 field: "f".to_string(),
4292 operator: Equal,
4293 value: Value::Bytes(vec![1, 2, 3]),
4294 };
4295 assert!(a.less_than(&a, true).unwrap());
4296 assert!(!a.less_than(&a, false).unwrap());
4297 }
4298
4299 #[test]
4300 fn less_than_text_equal_values_with_allow_eq() {
4301 let a = WhereClause {
4302 field: "f".to_string(),
4303 operator: Equal,
4304 value: Value::Text("same".to_string()),
4305 };
4306 assert!(a.less_than(&a, true).unwrap());
4307 assert!(!a.less_than(&a, false).unwrap());
4308 }
4309
4310 #[test]
4311 fn less_than_float_equal_values_with_allow_eq() {
4312 let a = WhereClause {
4313 field: "f".to_string(),
4314 operator: Equal,
4315 value: Value::Float(2.5),
4316 };
4317 assert!(a.less_than(&a, true).unwrap());
4318 assert!(!a.less_than(&a, false).unwrap());
4319 }
4320
4321 #[test]
4322 fn less_than_mismatched_integer_types_returns_error() {
4323 let a = WhereClause {
4324 field: "f".to_string(),
4325 operator: Equal,
4326 value: Value::U64(1),
4327 };
4328 let b = WhereClause {
4329 field: "f".to_string(),
4330 operator: Equal,
4331 value: Value::I64(1),
4332 };
4333 assert!(a.less_than(&b, false).is_err());
4334 }
4335
4336 #[test]
4337 fn less_than_bool_vs_bool_returns_error() {
4338 let a = WhereClause {
4339 field: "f".to_string(),
4340 operator: Equal,
4341 value: Value::Bool(true),
4342 };
4343 let b = WhereClause {
4344 field: "f".to_string(),
4345 operator: Equal,
4346 value: Value::Bool(false),
4347 };
4348 assert!(a.less_than(&b, false).is_err());
4349 }
4350
4351 #[test]
4354 fn value_shape_ok_between_with_three_elements_rejected() {
4355 use super::WhereOperator;
4356 use dpp::data_contract::document_type::DocumentPropertyType;
4357
4358 let three = Value::Array(vec![Value::I64(1), Value::I64(5), Value::I64(10)]);
4359 assert!(!WhereOperator::Between.value_shape_ok(&three, &DocumentPropertyType::I64));
4360 }
4361
4362 #[test]
4363 fn value_shape_ok_between_with_empty_array_rejected() {
4364 use super::WhereOperator;
4365 use dpp::data_contract::document_type::DocumentPropertyType;
4366
4367 let empty = Value::Array(vec![]);
4368 assert!(!WhereOperator::Between.value_shape_ok(&empty, &DocumentPropertyType::I64));
4369 }
4370
4371 #[test]
4372 fn value_shape_ok_between_for_f64_property_requires_numeric_elements() {
4373 use super::WhereOperator;
4374 use dpp::data_contract::document_type::DocumentPropertyType;
4375
4376 let good = Value::Array(vec![Value::Float(1.0), Value::Float(10.0)]);
4377 assert!(WhereOperator::Between.value_shape_ok(&good, &DocumentPropertyType::F64));
4378
4379 let also_good = Value::Array(vec![Value::I64(1), Value::I64(10)]);
4380 assert!(WhereOperator::Between.value_shape_ok(&also_good, &DocumentPropertyType::F64));
4381
4382 let bad = Value::Array(vec![Value::Text("a".into()), Value::Text("b".into())]);
4383 assert!(!WhereOperator::Between.value_shape_ok(&bad, &DocumentPropertyType::F64));
4384 }
4385
4386 #[test]
4387 fn value_shape_ok_between_for_string_property_requires_text_elements() {
4388 use super::WhereOperator;
4389 use dpp::data_contract::document_type::{DocumentPropertyType, StringPropertySizes};
4390
4391 let str_ty = DocumentPropertyType::String(StringPropertySizes {
4392 min_length: None,
4393 max_length: None,
4394 max_bytes: None,
4395 });
4396
4397 let good = Value::Array(vec![Value::Text("aaa".into()), Value::Text("zzz".into())]);
4398 assert!(WhereOperator::Between.value_shape_ok(&good, &str_ty));
4399
4400 let bad = Value::Array(vec![Value::I64(1), Value::I64(10)]);
4401 assert!(!WhereOperator::Between.value_shape_ok(&bad, &str_ty));
4402 }
4403
4404 #[test]
4405 fn value_shape_ok_between_exclude_left_with_non_array_rejected() {
4406 use super::WhereOperator;
4407 use dpp::data_contract::document_type::DocumentPropertyType;
4408
4409 assert!(!WhereOperator::BetweenExcludeLeft
4410 .value_shape_ok(&Value::I64(5), &DocumentPropertyType::I64));
4411 }
4412
4413 #[test]
4414 fn value_shape_ok_between_exclude_right_with_non_array_rejected() {
4415 use super::WhereOperator;
4416 use dpp::data_contract::document_type::DocumentPropertyType;
4417
4418 assert!(!WhereOperator::BetweenExcludeRight
4419 .value_shape_ok(&Value::I64(5), &DocumentPropertyType::I64));
4420 }
4421
4422 #[test]
4423 fn value_shape_ok_between_exclude_bounds_with_non_array_rejected() {
4424 use super::WhereOperator;
4425 use dpp::data_contract::document_type::DocumentPropertyType;
4426
4427 assert!(!WhereOperator::BetweenExcludeBounds
4428 .value_shape_ok(&Value::I64(5), &DocumentPropertyType::I64));
4429 }
4430
4431 #[test]
4432 fn value_shape_ok_range_accepts_all_integer_widths() {
4433 use super::WhereOperator;
4434 use dpp::data_contract::document_type::DocumentPropertyType;
4435
4436 let cases: Vec<(Value, DocumentPropertyType)> = vec![
4438 (Value::U8(1), DocumentPropertyType::U8),
4439 (Value::I8(-1), DocumentPropertyType::I8),
4440 (Value::U16(1), DocumentPropertyType::U16),
4441 (Value::I16(-1), DocumentPropertyType::I16),
4442 (Value::U32(1), DocumentPropertyType::U32),
4443 (Value::I32(-1), DocumentPropertyType::I32),
4444 (Value::U64(1), DocumentPropertyType::U64),
4445 (Value::I64(-1), DocumentPropertyType::I64),
4446 (Value::U128(1), DocumentPropertyType::U128),
4447 (Value::I128(-1), DocumentPropertyType::I128),
4448 ];
4449 for (val, ty) in cases {
4450 assert!(
4451 WhereOperator::GreaterThan.value_shape_ok(&val, &ty),
4452 "GreaterThan should accept integer value for {:?}",
4453 ty
4454 );
4455 assert!(
4456 WhereOperator::LessThanOrEquals.value_shape_ok(&val, &ty),
4457 "LessThanOrEquals should accept integer value for {:?}",
4458 ty
4459 );
4460 }
4461 }
4462
4463 #[test]
4464 fn value_shape_ok_range_rejects_bool_for_integer_type() {
4465 use super::WhereOperator;
4466 use dpp::data_contract::document_type::DocumentPropertyType;
4467
4468 assert!(!WhereOperator::GreaterThan
4469 .value_shape_ok(&Value::Bool(true), &DocumentPropertyType::U64));
4470 }
4471
4472 #[test]
4473 fn value_shape_ok_in_rejects_text() {
4474 use super::WhereOperator;
4475 use dpp::data_contract::document_type::DocumentPropertyType;
4476
4477 assert!(!WhereOperator::In
4478 .value_shape_ok(&Value::Text("not-array".into()), &DocumentPropertyType::U64));
4479 }
4480
4481 #[test]
4484 fn value_clause_matches_value_less_than() {
4485 let clause = ValueClause {
4486 operator: LessThan,
4487 value: Value::I64(50),
4488 };
4489 assert!(clause.matches_value(&Value::I64(30)));
4490 assert!(!clause.matches_value(&Value::I64(50)));
4491 assert!(!clause.matches_value(&Value::I64(60)));
4492 }
4493
4494 #[test]
4495 fn value_clause_matches_value_less_than_or_equals() {
4496 let clause = ValueClause {
4497 operator: LessThanOrEquals,
4498 value: Value::I64(50),
4499 };
4500 assert!(clause.matches_value(&Value::I64(30)));
4501 assert!(clause.matches_value(&Value::I64(50)));
4502 assert!(!clause.matches_value(&Value::I64(51)));
4503 }
4504
4505 #[test]
4506 fn value_clause_matches_value_greater_than_or_equals() {
4507 let clause = ValueClause {
4508 operator: GreaterThanOrEquals,
4509 value: Value::I64(10),
4510 };
4511 assert!(clause.matches_value(&Value::I64(10)));
4512 assert!(clause.matches_value(&Value::I64(100)));
4513 assert!(!clause.matches_value(&Value::I64(9)));
4514 }
4515
4516 #[test]
4517 fn value_clause_matches_between_inclusive() {
4518 let clause = ValueClause {
4519 operator: Between,
4520 value: Value::Array(vec![Value::U64(10), Value::U64(20)]),
4521 };
4522 assert!(clause.matches_value(&Value::U64(10)));
4523 assert!(clause.matches_value(&Value::U64(15)));
4524 assert!(clause.matches_value(&Value::U64(20)));
4525 assert!(!clause.matches_value(&Value::U64(9)));
4526 assert!(!clause.matches_value(&Value::U64(21)));
4527 }
4528
4529 #[test]
4530 fn value_clause_matches_between_exclude_bounds() {
4531 let clause = ValueClause {
4532 operator: BetweenExcludeBounds,
4533 value: Value::Array(vec![Value::U64(10), Value::U64(20)]),
4534 };
4535 assert!(!clause.matches_value(&Value::U64(10)));
4536 assert!(clause.matches_value(&Value::U64(15)));
4537 assert!(!clause.matches_value(&Value::U64(20)));
4538 }
4539
4540 #[test]
4541 fn value_clause_matches_between_exclude_left() {
4542 let clause = ValueClause {
4543 operator: BetweenExcludeLeft,
4544 value: Value::Array(vec![Value::U64(10), Value::U64(20)]),
4545 };
4546 assert!(!clause.matches_value(&Value::U64(10)));
4547 assert!(clause.matches_value(&Value::U64(11)));
4548 assert!(clause.matches_value(&Value::U64(20)));
4549 }
4550
4551 #[test]
4552 fn value_clause_matches_between_exclude_right() {
4553 let clause = ValueClause {
4554 operator: BetweenExcludeRight,
4555 value: Value::Array(vec![Value::U64(10), Value::U64(20)]),
4556 };
4557 assert!(clause.matches_value(&Value::U64(10)));
4558 assert!(clause.matches_value(&Value::U64(19)));
4559 assert!(!clause.matches_value(&Value::U64(20)));
4560 }
4561
4562 #[test]
4563 fn value_clause_in_with_bytes() {
4564 let clause = ValueClause {
4565 operator: In,
4566 value: Value::Bytes(vec![5, 10, 15]),
4567 };
4568 assert!(clause.matches_value(&Value::U8(10)));
4569 assert!(!clause.matches_value(&Value::U8(20)));
4570 assert!(!clause.matches_value(&Value::I64(10)));
4572 }
4573
4574 #[test]
4575 fn value_clause_starts_with_non_text_returns_false() {
4576 let clause = ValueClause {
4577 operator: super::StartsWith,
4578 value: Value::Text("he".to_string()),
4579 };
4580 assert!(!clause.matches_value(&Value::I64(42)));
4581 }
4582
4583 #[test]
4586 fn where_clause_matches_value_between() {
4587 let clause = WhereClause {
4588 field: "price".to_string(),
4589 operator: Between,
4590 value: Value::Array(vec![Value::U64(100), Value::U64(500)]),
4591 };
4592 assert!(clause.matches_value(&Value::U64(100)));
4593 assert!(clause.matches_value(&Value::U64(300)));
4594 assert!(clause.matches_value(&Value::U64(500)));
4595 assert!(!clause.matches_value(&Value::U64(99)));
4596 assert!(!clause.matches_value(&Value::U64(501)));
4597 }
4598
4599 #[test]
4600 fn where_clause_matches_value_in() {
4601 let clause = WhereClause {
4602 field: "status".to_string(),
4603 operator: In,
4604 value: Value::Array(vec![
4605 Value::Text("a".to_string()),
4606 Value::Text("b".to_string()),
4607 ]),
4608 };
4609 assert!(clause.matches_value(&Value::Text("a".to_string())));
4610 assert!(clause.matches_value(&Value::Text("b".to_string())));
4611 assert!(!clause.matches_value(&Value::Text("c".to_string())));
4612 }
4613
4614 #[test]
4615 fn where_clause_matches_value_starts_with() {
4616 let clause = WhereClause {
4617 field: "name".to_string(),
4618 operator: super::StartsWith,
4619 value: Value::Text("pre".to_string()),
4620 };
4621 assert!(clause.matches_value(&Value::Text("prefix_value".to_string())));
4622 assert!(!clause.matches_value(&Value::Text("no_match".to_string())));
4623 }
4624
4625 #[test]
4628 fn eval_greater_than_with_text() {
4629 assert!(GreaterThan.eval(
4630 &Value::Text("banana".to_string()),
4631 &Value::Text("apple".to_string())
4632 ));
4633 assert!(!GreaterThan.eval(
4634 &Value::Text("apple".to_string()),
4635 &Value::Text("banana".to_string())
4636 ));
4637 }
4638
4639 #[test]
4640 fn eval_less_than_with_text() {
4641 assert!(LessThan.eval(
4642 &Value::Text("apple".to_string()),
4643 &Value::Text("banana".to_string())
4644 ));
4645 assert!(!LessThan.eval(
4646 &Value::Text("banana".to_string()),
4647 &Value::Text("apple".to_string())
4648 ));
4649 }
4650
4651 #[test]
4652 fn eval_between_with_text() {
4653 let bounds = Value::Array(vec![
4654 Value::Text("b".to_string()),
4655 Value::Text("d".to_string()),
4656 ]);
4657 assert!(Between.eval(&Value::Text("b".to_string()), &bounds));
4658 assert!(Between.eval(&Value::Text("c".to_string()), &bounds));
4659 assert!(Between.eval(&Value::Text("d".to_string()), &bounds));
4660 assert!(!Between.eval(&Value::Text("a".to_string()), &bounds));
4661 assert!(!Between.eval(&Value::Text("e".to_string()), &bounds));
4662 }
4663
4664 #[test]
4665 fn eval_equal_with_text() {
4666 assert!(Equal.eval(
4667 &Value::Text("same".to_string()),
4668 &Value::Text("same".to_string())
4669 ));
4670 assert!(!Equal.eval(
4671 &Value::Text("one".to_string()),
4672 &Value::Text("two".to_string())
4673 ));
4674 }
4675
4676 #[test]
4677 fn eval_in_with_empty_array_returns_false() {
4678 let arr = Value::Array(vec![]);
4679 assert!(!In.eval(&Value::I64(1), &arr));
4680 }
4681
4682 #[test]
4683 fn eval_starts_with_empty_prefix_matches_everything() {
4684 assert!(super::StartsWith.eval(
4685 &Value::Text("anything".to_string()),
4686 &Value::Text("".to_string())
4687 ));
4688 }
4689}