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