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