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