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)]
1704mod tests {
1705 use crate::error::query::QuerySyntaxError;
1706 use crate::query::conditions::WhereClause;
1707 use crate::query::conditions::{
1708 Between, BetweenExcludeBounds, BetweenExcludeLeft, BetweenExcludeRight, Equal, GreaterThan,
1709 GreaterThanOrEquals, In, LessThan, LessThanOrEquals, ValueClause,
1710 };
1711 use crate::query::InternalClauses;
1712 use dpp::data_contract::accessors::v0::DataContractV0Getters;
1713 use dpp::platform_value::Value;
1714 use dpp::tests::fixtures::get_data_contract_fixture;
1715 use dpp::version::LATEST_PLATFORM_VERSION;
1716
1717 #[test]
1718 fn test_allowed_sup_query_pairs() {
1719 let allowed_pairs_test_cases = [
1720 [GreaterThan, LessThan],
1721 [GreaterThan, LessThanOrEquals],
1722 [GreaterThanOrEquals, LessThanOrEquals],
1723 ];
1724 for query_pair in allowed_pairs_test_cases {
1725 let where_clauses = vec![
1726 WhereClause {
1727 field: "a".to_string(),
1728 operator: *query_pair.first().unwrap(),
1729 value: Value::Float(0.0),
1730 },
1731 WhereClause {
1732 field: "a".to_string(),
1733 operator: *query_pair.get(1).unwrap(),
1734 value: Value::Float(1.0),
1735 },
1736 ];
1737 let (_, range_clause, _) = WhereClause::group_clauses(&where_clauses)
1738 .expect("expected to have groupable pair");
1739 range_clause.expect("expected to have range clause returned");
1740 }
1741 }
1742
1743 #[test]
1744 fn test_allowed_inf_query_pairs() {
1745 let allowed_pairs_test_cases = [
1746 [LessThan, GreaterThan],
1747 [LessThan, GreaterThanOrEquals],
1748 [LessThanOrEquals, GreaterThanOrEquals],
1749 ];
1750 for query_pair in allowed_pairs_test_cases {
1751 let where_clauses = vec![
1752 WhereClause {
1753 field: "a".to_string(),
1754 operator: *query_pair.first().unwrap(),
1755 value: Value::Float(1.0),
1756 },
1757 WhereClause {
1758 field: "a".to_string(),
1759 operator: *query_pair.get(1).unwrap(),
1760 value: Value::Float(0.0),
1761 },
1762 ];
1763 let (_, range_clause, _) = WhereClause::group_clauses(&where_clauses)
1764 .expect("expected to have groupable pair");
1765 range_clause.expect("expected to have range clause returned");
1766 }
1767 }
1768
1769 #[test]
1770 fn test_query_pairs_incoherent_same_value() {
1771 let allowed_pairs_test_cases = [[LessThan, GreaterThan], [GreaterThan, LessThan]];
1772 for query_pair in allowed_pairs_test_cases {
1773 let where_clauses = vec![
1774 WhereClause {
1775 field: "a".to_string(),
1776 operator: *query_pair.first().unwrap(),
1777 value: Value::Float(1.0),
1778 },
1779 WhereClause {
1780 field: "a".to_string(),
1781 operator: *query_pair.get(1).unwrap(),
1782 value: Value::Float(1.0),
1783 },
1784 ];
1785 WhereClause::group_clauses(&where_clauses)
1786 .expect_err("expected to have an error returned");
1787 }
1788 }
1789
1790 #[test]
1791 fn test_different_fields_grouping_causes_error() {
1792 let where_clauses = vec![
1793 WhereClause {
1794 field: "a".to_string(),
1795 operator: LessThan,
1796 value: Value::Float(0.0),
1797 },
1798 WhereClause {
1799 field: "b".to_string(),
1800 operator: GreaterThan,
1801 value: Value::Float(1.0),
1802 },
1803 ];
1804 WhereClause::group_clauses(&where_clauses)
1805 .expect_err("different fields should not be groupable");
1806 }
1807
1808 #[test]
1809 fn test_restricted_query_pairs_causes_error() {
1810 let restricted_pairs_test_cases = [
1811 [Equal, LessThan],
1812 [Equal, GreaterThan],
1813 [In, LessThan],
1814 [Equal, GreaterThan],
1815 [LessThanOrEquals, LessThanOrEquals],
1816 [LessThan, LessThan],
1817 [LessThan, LessThanOrEquals],
1818 [GreaterThan, GreaterThan],
1819 [GreaterThan, GreaterThanOrEquals],
1820 [GreaterThanOrEquals, GreaterThanOrEquals],
1821 [Equal, Equal],
1822 ];
1823 for query_pair in restricted_pairs_test_cases {
1824 let where_clauses = vec![
1825 WhereClause {
1826 field: "a".to_string(),
1827 operator: *query_pair.first().unwrap(),
1828 value: Value::Float(0.0),
1829 },
1830 WhereClause {
1831 field: "a".to_string(),
1832 operator: *query_pair.get(1).unwrap(),
1833 value: Value::Float(1.0),
1834 },
1835 ];
1836 WhereClause::group_clauses(&where_clauses)
1837 .expect_err("expected to not have a groupable pair");
1838 }
1839 }
1840
1841 #[test]
1842 fn validate_rejects_equality_with_wrong_type_for_string_field() {
1843 let fixture = get_data_contract_fixture(None, 0, LATEST_PLATFORM_VERSION.protocol_version);
1844 let contract = fixture.data_contract_owned();
1845 let doc_type = contract
1846 .document_type_for_name("niceDocument")
1847 .expect("doc type exists");
1848
1849 let clause = WhereClause {
1850 field: "name".to_string(),
1851 operator: Equal,
1852 value: Value::Identifier([1u8; 32]),
1853 };
1854 let res = clause.validate_against_schema(doc_type);
1855 assert!(res.is_err());
1856 assert!(matches!(
1857 res.first_error(),
1858 Some(QuerySyntaxError::InvalidWhereClauseComponents(_))
1859 ));
1860 }
1861
1862 #[test]
1863 fn validate_rejects_in_with_wrong_element_types() {
1864 let fixture = get_data_contract_fixture(None, 0, LATEST_PLATFORM_VERSION.protocol_version);
1865 let contract = fixture.data_contract_owned();
1866 let doc_type = contract
1867 .document_type_for_name("indexedDocument")
1868 .expect("doc type exists");
1869
1870 let clause = WhereClause {
1871 field: "firstName".to_string(),
1872 operator: In,
1873 value: Value::Array(vec![
1874 Value::Text("alice".to_string()),
1875 Value::Identifier([2u8; 32]),
1876 ]),
1877 };
1878 let res = clause.validate_against_schema(doc_type);
1879 assert!(res.is_err());
1880 assert!(matches!(
1881 res.first_error(),
1882 Some(QuerySyntaxError::InvalidWhereClauseComponents(_))
1883 ));
1884 }
1885
1886 #[test]
1887 fn validate_rejects_primary_key_in_with_non_identifiers() {
1888 let fixture = get_data_contract_fixture(None, 0, LATEST_PLATFORM_VERSION.protocol_version);
1889 let contract = fixture.data_contract_owned();
1890 let doc_type = contract
1891 .document_type_for_name("niceDocument")
1892 .expect("doc type exists");
1893
1894 let clauses = InternalClauses {
1895 primary_key_in_clause: Some(WhereClause {
1896 field: "$id".to_string(),
1897 operator: In,
1898 value: Value::Array(vec![
1899 Value::Text("a".to_string()),
1900 Value::Text("b".to_string()),
1901 ]),
1902 }),
1903 ..Default::default()
1904 };
1905
1906 let res = clauses.validate_against_schema(doc_type);
1907 assert!(res.is_err());
1908 assert!(matches!(
1909 res.first_error(),
1910 Some(QuerySyntaxError::InvalidWhereClauseComponents(_))
1911 ));
1912 }
1913
1914 #[test]
1915 fn validate_rejects_date_with_float_equality() {
1916 let fixture = get_data_contract_fixture(None, 0, LATEST_PLATFORM_VERSION.protocol_version);
1917 let contract = fixture.data_contract_owned();
1918 let doc_type = contract
1919 .document_type_for_name("uniqueDates")
1920 .expect("doc type exists");
1921
1922 let clause = WhereClause {
1923 field: "$createdAt".to_string(),
1924 operator: Equal,
1925 value: Value::Float(1.23),
1926 };
1927 let res = clause.validate_against_schema(doc_type);
1928 assert!(res.is_err());
1929 assert!(matches!(
1930 res.first_error(),
1931 Some(QuerySyntaxError::InvalidWhereClauseComponents(_))
1932 ));
1933 }
1934
1935 #[test]
1936 fn validate_rejects_in_bytes_for_string_field() {
1937 let fixture = get_data_contract_fixture(None, 0, LATEST_PLATFORM_VERSION.protocol_version);
1938 let contract = fixture.data_contract_owned();
1939 let doc_type = contract
1940 .document_type_for_name("niceDocument")
1941 .expect("doc type exists");
1942
1943 let clause = WhereClause {
1945 field: "name".to_string(),
1946 operator: In,
1947 value: Value::Bytes(vec![1, 2, 3]),
1948 };
1949 let res = clause.validate_against_schema(doc_type);
1950 assert!(res.is_err());
1951 }
1952
1953 #[test]
1954 fn validate_accepts_meta_owner_id_in_identifiers() {
1955 let fixture = get_data_contract_fixture(None, 0, LATEST_PLATFORM_VERSION.protocol_version);
1956 let contract = fixture.data_contract_owned();
1957 let doc_type = contract
1958 .document_type_for_name("niceDocument")
1959 .expect("doc type exists");
1960
1961 let clause = WhereClause {
1962 field: "$ownerId".to_string(),
1963 operator: In,
1964 value: Value::Array(vec![
1965 Value::Identifier([1u8; 32]),
1966 Value::Identifier([2u8; 32]),
1967 ]),
1968 };
1969 let res = clause.validate_against_schema(doc_type);
1970 assert!(res.is_valid());
1971 }
1972
1973 #[test]
1974 fn validate_accepts_meta_created_at_between_integers() {
1975 let fixture = get_data_contract_fixture(None, 0, LATEST_PLATFORM_VERSION.protocol_version);
1976 let contract = fixture.data_contract_owned();
1977 let doc_type = contract
1978 .document_type_for_name("uniqueDates")
1979 .expect("doc type exists");
1980
1981 let clause = WhereClause {
1982 field: "$createdAt".to_string(),
1983 operator: crate::query::conditions::Between,
1984 value: Value::Array(vec![Value::U64(1000), Value::U64(2000)]),
1985 };
1986 let res = clause.validate_against_schema(doc_type);
1987 assert!(res.is_valid());
1988 }
1989
1990 #[test]
1991 fn validate_rejects_between_variants_with_equal_bounds() {
1992 let fixture = get_data_contract_fixture(None, 0, LATEST_PLATFORM_VERSION.protocol_version);
1993 let contract = fixture.data_contract_owned();
1994 let doc_type = contract
1995 .document_type_for_name("uniqueDates")
1996 .expect("doc type exists");
1997
1998 for operator in [
1999 Between,
2000 BetweenExcludeBounds,
2001 BetweenExcludeLeft,
2002 BetweenExcludeRight,
2003 ] {
2004 let clause = WhereClause {
2005 field: "$createdAt".to_string(),
2006 operator,
2007 value: Value::Array(vec![Value::U64(1000), Value::U64(1000)]),
2008 };
2009
2010 let res = clause.validate_against_schema(doc_type);
2011 assert!(
2012 res.is_err(),
2013 "{operator:?} should reject equal bounds during validation"
2014 );
2015 assert!(matches!(
2016 res.first_error(),
2017 Some(QuerySyntaxError::InvalidBetweenClause(_))
2018 ));
2019 }
2020 }
2021
2022 #[test]
2023 fn value_clause_between_variants_do_not_match_equal_bounds() {
2024 let equal_bounds = Value::Array(vec![Value::U64(1000), Value::U64(1000)]);
2025 let value_to_test = Value::U64(1000);
2026
2027 for operator in [
2028 Between,
2029 BetweenExcludeBounds,
2030 BetweenExcludeLeft,
2031 BetweenExcludeRight,
2032 ] {
2033 let clause = ValueClause {
2034 operator,
2035 value: equal_bounds.clone(),
2036 };
2037
2038 assert!(
2039 !clause.matches_value(&value_to_test),
2040 "{operator:?} should not match when bounds are equal"
2041 );
2042 }
2043 }
2044
2045 #[test]
2046 fn validate_rejects_meta_revision_float_equality() {
2047 let fixture = get_data_contract_fixture(None, 0, LATEST_PLATFORM_VERSION.protocol_version);
2048 let contract = fixture.data_contract_owned();
2049 let doc_type = contract
2050 .document_type_for_name("niceDocument")
2051 .expect("doc type exists");
2052
2053 let clause = WhereClause {
2054 field: "$revision".to_string(),
2055 operator: Equal,
2056 value: Value::Float(3.15),
2057 };
2058 let res = clause.validate_against_schema(doc_type);
2059 assert!(res.is_err());
2060 }
2061
2062 #[test]
2063 fn validate_accepts_meta_created_at_block_height_range() {
2064 let fixture = get_data_contract_fixture(None, 0, LATEST_PLATFORM_VERSION.protocol_version);
2065 let contract = fixture.data_contract_owned();
2066 let doc_type = contract
2067 .document_type_for_name("uniqueDates")
2068 .expect("doc type exists");
2069
2070 let clause = WhereClause {
2071 field: "$createdAtBlockHeight".to_string(),
2072 operator: GreaterThanOrEquals,
2073 value: Value::U64(100),
2074 };
2075 let res = clause.validate_against_schema(doc_type);
2076 assert!(res.is_valid());
2077 }
2078
2079 #[test]
2080 fn validate_accepts_meta_data_contract_id_equality() {
2081 let fixture = get_data_contract_fixture(None, 0, LATEST_PLATFORM_VERSION.protocol_version);
2082 let contract = fixture.data_contract_owned();
2083 let doc_type = contract
2084 .document_type_for_name("niceDocument")
2085 .expect("doc type exists");
2086
2087 let clause = WhereClause {
2088 field: "$dataContractId".to_string(),
2089 operator: Equal,
2090 value: Value::Identifier([3u8; 32]),
2091 };
2092 let res = clause.validate_against_schema(doc_type);
2093 assert!(res.is_valid());
2094 }
2095
2096 #[test]
2099 fn allows_flip_returns_true_for_comparison_operators() {
2100 assert!(Equal.allows_flip());
2101 assert!(GreaterThan.allows_flip());
2102 assert!(GreaterThanOrEquals.allows_flip());
2103 assert!(LessThan.allows_flip());
2104 assert!(LessThanOrEquals.allows_flip());
2105 }
2106
2107 #[test]
2108 fn allows_flip_returns_false_for_non_flippable_operators() {
2109 assert!(!Between.allows_flip());
2110 assert!(!BetweenExcludeBounds.allows_flip());
2111 assert!(!BetweenExcludeLeft.allows_flip());
2112 assert!(!BetweenExcludeRight.allows_flip());
2113 assert!(!In.allows_flip());
2114 assert!(!super::StartsWith.allows_flip());
2115 }
2116
2117 #[test]
2120 fn flip_equal_stays_equal() {
2121 assert_eq!(Equal.flip().unwrap(), Equal);
2122 }
2123
2124 #[test]
2125 fn flip_greater_than_becomes_less_than() {
2126 assert_eq!(GreaterThan.flip().unwrap(), LessThan);
2127 }
2128
2129 #[test]
2130 fn flip_greater_than_or_equals_becomes_less_than_or_equals() {
2131 assert_eq!(GreaterThanOrEquals.flip().unwrap(), LessThanOrEquals);
2132 }
2133
2134 #[test]
2135 fn flip_less_than_becomes_greater_than() {
2136 assert_eq!(LessThan.flip().unwrap(), GreaterThan);
2137 }
2138
2139 #[test]
2140 fn flip_less_than_or_equals_becomes_greater_than_or_equals() {
2141 assert_eq!(LessThanOrEquals.flip().unwrap(), GreaterThanOrEquals);
2142 }
2143
2144 #[test]
2145 fn flip_between_returns_error() {
2146 assert!(Between.flip().is_err());
2147 }
2148
2149 #[test]
2150 fn flip_between_exclude_bounds_returns_error() {
2151 assert!(BetweenExcludeBounds.flip().is_err());
2152 }
2153
2154 #[test]
2155 fn flip_between_exclude_left_returns_error() {
2156 assert!(BetweenExcludeLeft.flip().is_err());
2157 }
2158
2159 #[test]
2160 fn flip_between_exclude_right_returns_error() {
2161 assert!(BetweenExcludeRight.flip().is_err());
2162 }
2163
2164 #[test]
2165 fn flip_in_returns_error() {
2166 assert!(In.flip().is_err());
2167 }
2168
2169 #[test]
2170 fn flip_starts_with_returns_error() {
2171 assert!(super::StartsWith.flip().is_err());
2172 }
2173
2174 #[test]
2177 fn is_range_false_for_equal() {
2178 assert!(!Equal.is_range());
2179 }
2180
2181 #[test]
2182 fn is_range_true_for_all_range_operators() {
2183 assert!(GreaterThan.is_range());
2184 assert!(GreaterThanOrEquals.is_range());
2185 assert!(LessThan.is_range());
2186 assert!(LessThanOrEquals.is_range());
2187 assert!(Between.is_range());
2188 assert!(BetweenExcludeBounds.is_range());
2189 assert!(BetweenExcludeLeft.is_range());
2190 assert!(BetweenExcludeRight.is_range());
2191 assert!(In.is_range());
2192 assert!(super::StartsWith.is_range());
2193 }
2194
2195 #[test]
2198 fn from_string_parses_equality_operators() {
2199 use super::WhereOperator;
2200 assert_eq!(WhereOperator::from_string("="), Some(Equal));
2201 assert_eq!(WhereOperator::from_string("=="), Some(Equal));
2202 }
2203
2204 #[test]
2205 fn from_string_parses_comparison_operators() {
2206 use super::WhereOperator;
2207 assert_eq!(WhereOperator::from_string(">"), Some(GreaterThan));
2208 assert_eq!(WhereOperator::from_string(">="), Some(GreaterThanOrEquals));
2209 assert_eq!(WhereOperator::from_string("<"), Some(LessThan));
2210 assert_eq!(WhereOperator::from_string("<="), Some(LessThanOrEquals));
2211 }
2212
2213 #[test]
2214 fn from_string_parses_between_variants() {
2215 use super::WhereOperator;
2216 assert_eq!(WhereOperator::from_string("Between"), Some(Between));
2217 assert_eq!(WhereOperator::from_string("between"), Some(Between));
2218 assert_eq!(
2219 WhereOperator::from_string("BetweenExcludeBounds"),
2220 Some(BetweenExcludeBounds)
2221 );
2222 assert_eq!(
2223 WhereOperator::from_string("betweenExcludeBounds"),
2224 Some(BetweenExcludeBounds)
2225 );
2226 assert_eq!(
2227 WhereOperator::from_string("betweenexcludebounds"),
2228 Some(BetweenExcludeBounds)
2229 );
2230 assert_eq!(
2231 WhereOperator::from_string("between_exclude_bounds"),
2232 Some(BetweenExcludeBounds)
2233 );
2234 assert_eq!(
2235 WhereOperator::from_string("BetweenExcludeLeft"),
2236 Some(BetweenExcludeLeft)
2237 );
2238 assert_eq!(
2239 WhereOperator::from_string("betweenExcludeLeft"),
2240 Some(BetweenExcludeLeft)
2241 );
2242 assert_eq!(
2243 WhereOperator::from_string("betweenexcludeleft"),
2244 Some(BetweenExcludeLeft)
2245 );
2246 assert_eq!(
2247 WhereOperator::from_string("between_exclude_left"),
2248 Some(BetweenExcludeLeft)
2249 );
2250 assert_eq!(
2251 WhereOperator::from_string("BetweenExcludeRight"),
2252 Some(BetweenExcludeRight)
2253 );
2254 assert_eq!(
2255 WhereOperator::from_string("betweenExcludeRight"),
2256 Some(BetweenExcludeRight)
2257 );
2258 assert_eq!(
2259 WhereOperator::from_string("betweenexcluderight"),
2260 Some(BetweenExcludeRight)
2261 );
2262 assert_eq!(
2263 WhereOperator::from_string("between_exclude_right"),
2264 Some(BetweenExcludeRight)
2265 );
2266 }
2267
2268 #[test]
2269 fn from_string_parses_in_operator() {
2270 use super::WhereOperator;
2271 assert_eq!(WhereOperator::from_string("In"), Some(In));
2272 assert_eq!(WhereOperator::from_string("in"), Some(In));
2273 }
2274
2275 #[test]
2276 fn from_string_parses_starts_with_operator() {
2277 use super::WhereOperator;
2278 assert_eq!(
2279 WhereOperator::from_string("StartsWith"),
2280 Some(super::StartsWith)
2281 );
2282 assert_eq!(
2283 WhereOperator::from_string("startsWith"),
2284 Some(super::StartsWith)
2285 );
2286 assert_eq!(
2287 WhereOperator::from_string("startswith"),
2288 Some(super::StartsWith)
2289 );
2290 assert_eq!(
2291 WhereOperator::from_string("starts_with"),
2292 Some(super::StartsWith)
2293 );
2294 }
2295
2296 #[test]
2297 fn from_string_returns_none_for_unknown() {
2298 use super::WhereOperator;
2299 assert_eq!(WhereOperator::from_string("LIKE"), None);
2300 assert_eq!(WhereOperator::from_string("!="), None);
2301 assert_eq!(WhereOperator::from_string(""), None);
2302 }
2303
2304 #[test]
2307 fn from_sql_operator_maps_known_operators() {
2308 use super::WhereOperator;
2309 use sqlparser::ast::BinaryOperator;
2310 assert_eq!(
2311 WhereOperator::from_sql_operator(BinaryOperator::Eq),
2312 Some(Equal)
2313 );
2314 assert_eq!(
2315 WhereOperator::from_sql_operator(BinaryOperator::Gt),
2316 Some(GreaterThan)
2317 );
2318 assert_eq!(
2319 WhereOperator::from_sql_operator(BinaryOperator::GtEq),
2320 Some(GreaterThanOrEquals)
2321 );
2322 assert_eq!(
2323 WhereOperator::from_sql_operator(BinaryOperator::Lt),
2324 Some(LessThan)
2325 );
2326 assert_eq!(
2327 WhereOperator::from_sql_operator(BinaryOperator::LtEq),
2328 Some(LessThanOrEquals)
2329 );
2330 }
2331
2332 #[test]
2333 fn from_sql_operator_returns_none_for_unsupported() {
2334 use super::WhereOperator;
2335 use sqlparser::ast::BinaryOperator;
2336 assert_eq!(
2337 WhereOperator::from_sql_operator(BinaryOperator::NotEq),
2338 None
2339 );
2340 assert_eq!(WhereOperator::from_sql_operator(BinaryOperator::Plus), None);
2341 }
2342
2343 #[test]
2346 fn eval_equal_matches_identical_values() {
2347 assert!(Equal.eval(&Value::I64(42), &Value::I64(42)));
2348 assert!(!Equal.eval(&Value::I64(42), &Value::I64(43)));
2349 }
2350
2351 #[test]
2352 fn eval_greater_than() {
2353 assert!(GreaterThan.eval(&Value::I64(10), &Value::I64(5)));
2354 assert!(!GreaterThan.eval(&Value::I64(5), &Value::I64(10)));
2355 assert!(!GreaterThan.eval(&Value::I64(5), &Value::I64(5)));
2356 }
2357
2358 #[test]
2359 fn eval_greater_than_or_equals() {
2360 assert!(GreaterThanOrEquals.eval(&Value::I64(10), &Value::I64(5)));
2361 assert!(GreaterThanOrEquals.eval(&Value::I64(5), &Value::I64(5)));
2362 assert!(!GreaterThanOrEquals.eval(&Value::I64(4), &Value::I64(5)));
2363 }
2364
2365 #[test]
2366 fn eval_less_than() {
2367 assert!(LessThan.eval(&Value::I64(3), &Value::I64(5)));
2368 assert!(!LessThan.eval(&Value::I64(5), &Value::I64(3)));
2369 assert!(!LessThan.eval(&Value::I64(5), &Value::I64(5)));
2370 }
2371
2372 #[test]
2373 fn eval_less_than_or_equals() {
2374 assert!(LessThanOrEquals.eval(&Value::I64(3), &Value::I64(5)));
2375 assert!(LessThanOrEquals.eval(&Value::I64(5), &Value::I64(5)));
2376 assert!(!LessThanOrEquals.eval(&Value::I64(6), &Value::I64(5)));
2377 }
2378
2379 #[test]
2380 fn eval_in_with_array() {
2381 let arr = Value::Array(vec![Value::I64(1), Value::I64(2), Value::I64(3)]);
2382 assert!(In.eval(&Value::I64(2), &arr));
2383 assert!(!In.eval(&Value::I64(4), &arr));
2384 }
2385
2386 #[test]
2387 fn eval_in_with_bytes() {
2388 let bytes = Value::Bytes(vec![10, 20, 30]);
2389 assert!(In.eval(&Value::U8(20), &bytes));
2390 assert!(!In.eval(&Value::U8(40), &bytes));
2391 assert!(!In.eval(&Value::I64(20), &bytes));
2393 }
2394
2395 #[test]
2396 fn eval_in_with_non_collection_returns_false() {
2397 assert!(!In.eval(&Value::I64(1), &Value::I64(1)));
2398 }
2399
2400 #[test]
2401 fn eval_between_inclusive() {
2402 let bounds = Value::Array(vec![Value::I64(10), Value::I64(20)]);
2403 assert!(Between.eval(&Value::I64(10), &bounds));
2404 assert!(Between.eval(&Value::I64(15), &bounds));
2405 assert!(Between.eval(&Value::I64(20), &bounds));
2406 assert!(!Between.eval(&Value::I64(9), &bounds));
2407 assert!(!Between.eval(&Value::I64(21), &bounds));
2408 }
2409
2410 #[test]
2411 fn eval_between_exclude_bounds() {
2412 let bounds = Value::Array(vec![Value::I64(10), Value::I64(20)]);
2413 assert!(!BetweenExcludeBounds.eval(&Value::I64(10), &bounds));
2414 assert!(BetweenExcludeBounds.eval(&Value::I64(15), &bounds));
2415 assert!(!BetweenExcludeBounds.eval(&Value::I64(20), &bounds));
2416 }
2417
2418 #[test]
2419 fn eval_between_exclude_left() {
2420 let bounds = Value::Array(vec![Value::I64(10), Value::I64(20)]);
2421 assert!(!BetweenExcludeLeft.eval(&Value::I64(10), &bounds));
2422 assert!(BetweenExcludeLeft.eval(&Value::I64(15), &bounds));
2423 assert!(BetweenExcludeLeft.eval(&Value::I64(20), &bounds));
2424 }
2425
2426 #[test]
2427 fn eval_between_exclude_right() {
2428 let bounds = Value::Array(vec![Value::I64(10), Value::I64(20)]);
2429 assert!(BetweenExcludeRight.eval(&Value::I64(10), &bounds));
2430 assert!(BetweenExcludeRight.eval(&Value::I64(15), &bounds));
2431 assert!(!BetweenExcludeRight.eval(&Value::I64(20), &bounds));
2432 }
2433
2434 #[test]
2435 fn eval_between_with_wrong_bound_order_returns_false() {
2436 let bounds = Value::Array(vec![Value::I64(20), Value::I64(10)]);
2438 assert!(!Between.eval(&Value::I64(15), &bounds));
2439 assert!(!BetweenExcludeBounds.eval(&Value::I64(15), &bounds));
2440 assert!(!BetweenExcludeLeft.eval(&Value::I64(15), &bounds));
2441 assert!(!BetweenExcludeRight.eval(&Value::I64(15), &bounds));
2442 }
2443
2444 #[test]
2445 fn eval_between_with_non_array_returns_false() {
2446 assert!(!Between.eval(&Value::I64(5), &Value::I64(10)));
2447 }
2448
2449 #[test]
2450 fn eval_between_with_wrong_array_len_returns_false() {
2451 let single = Value::Array(vec![Value::I64(10)]);
2452 assert!(!Between.eval(&Value::I64(10), &single));
2453 }
2454
2455 #[test]
2456 fn eval_starts_with_text() {
2457 assert!(super::StartsWith.eval(
2458 &Value::Text("hello world".to_string()),
2459 &Value::Text("hello".to_string())
2460 ));
2461 assert!(!super::StartsWith.eval(
2462 &Value::Text("hello world".to_string()),
2463 &Value::Text("world".to_string())
2464 ));
2465 }
2466
2467 #[test]
2468 fn eval_starts_with_non_text_returns_false() {
2469 assert!(!super::StartsWith.eval(&Value::I64(123), &Value::Text("1".to_string())));
2470 assert!(!super::StartsWith.eval(&Value::Text("hello".to_string()), &Value::I64(1)));
2471 }
2472
2473 #[test]
2476 fn display_formatting_for_all_operators() {
2477 assert_eq!(format!("{}", Equal), "=");
2478 assert_eq!(format!("{}", GreaterThan), ">");
2479 assert_eq!(format!("{}", GreaterThanOrEquals), ">=");
2480 assert_eq!(format!("{}", LessThan), "<");
2481 assert_eq!(format!("{}", LessThanOrEquals), "<=");
2482 assert_eq!(format!("{}", Between), "Between");
2483 assert_eq!(format!("{}", BetweenExcludeBounds), "BetweenExcludeBounds");
2484 assert_eq!(format!("{}", BetweenExcludeLeft), "BetweenExcludeLeft");
2485 assert_eq!(format!("{}", BetweenExcludeRight), "BetweenExcludeRight");
2486 assert_eq!(format!("{}", In), "In");
2487 assert_eq!(format!("{}", super::StartsWith), "StartsWith");
2488 }
2489
2490 #[test]
2493 fn where_operator_into_value() {
2494 let val: Value = Equal.into();
2495 assert_eq!(val, Value::Text("=".to_string()));
2496
2497 let val: Value = In.into();
2498 assert_eq!(val, Value::Text("In".to_string()));
2499 }
2500
2501 #[test]
2504 fn is_identifier_returns_true_for_dollar_id() {
2505 let clause = WhereClause {
2506 field: "$id".to_string(),
2507 operator: Equal,
2508 value: Value::I64(1),
2509 };
2510 assert!(clause.is_identifier());
2511 }
2512
2513 #[test]
2514 fn is_identifier_returns_false_for_other_fields() {
2515 let clause = WhereClause {
2516 field: "name".to_string(),
2517 operator: Equal,
2518 value: Value::I64(1),
2519 };
2520 assert!(!clause.is_identifier());
2521
2522 let clause = WhereClause {
2523 field: "$ownerId".to_string(),
2524 operator: Equal,
2525 value: Value::I64(1),
2526 };
2527 assert!(!clause.is_identifier());
2528 }
2529
2530 #[test]
2533 fn in_values_with_array() {
2534 let clause = WhereClause {
2535 field: "f".to_string(),
2536 operator: In,
2537 value: Value::Array(vec![Value::I64(1), Value::I64(2)]),
2538 };
2539 let result = clause.in_values();
2540 assert!(result.is_valid());
2541 let data = result.into_data().expect("should have data");
2542 assert_eq!(data.len(), 2);
2543 }
2544
2545 #[test]
2546 fn in_values_with_bytes() {
2547 let clause = WhereClause {
2548 field: "f".to_string(),
2549 operator: In,
2550 value: Value::Bytes(vec![10, 20]),
2551 };
2552 let result = clause.in_values();
2553 assert!(result.is_valid());
2554 let data = result.into_data().expect("should have data");
2555 assert_eq!(data.len(), 2);
2556 assert_eq!(data[0], Value::U8(10));
2557 assert_eq!(data[1], Value::U8(20));
2558 }
2559
2560 #[test]
2561 fn in_values_non_array_returns_error() {
2562 let clause = WhereClause {
2563 field: "f".to_string(),
2564 operator: In,
2565 value: Value::I64(42),
2566 };
2567 let result = clause.in_values();
2568 assert!(!result.is_valid());
2569 }
2570
2571 #[test]
2572 fn in_values_empty_array_returns_error() {
2573 let clause = WhereClause {
2574 field: "f".to_string(),
2575 operator: In,
2576 value: Value::Array(vec![]),
2577 };
2578 let result = clause.in_values();
2579 assert!(!result.is_valid());
2580 }
2581
2582 #[test]
2583 fn in_values_too_many_returns_error() {
2584 let values: Vec<Value> = (0..101).map(|i| Value::I64(i)).collect();
2585 let clause = WhereClause {
2586 field: "f".to_string(),
2587 operator: In,
2588 value: Value::Array(values),
2589 };
2590 let result = clause.in_values();
2591 assert!(!result.is_valid());
2592 }
2593
2594 #[test]
2595 fn in_values_with_duplicates_returns_error() {
2596 let clause = WhereClause {
2597 field: "f".to_string(),
2598 operator: In,
2599 value: Value::Array(vec![Value::I64(1), Value::I64(1)]),
2600 };
2601 let result = clause.in_values();
2602 assert!(!result.is_valid());
2603 }
2604
2605 #[test]
2608 fn less_than_with_i128_values() {
2609 let a = WhereClause {
2610 field: "f".to_string(),
2611 operator: Equal,
2612 value: Value::I128(5),
2613 };
2614 let b = WhereClause {
2615 field: "f".to_string(),
2616 operator: Equal,
2617 value: Value::I128(10),
2618 };
2619 assert!(a.less_than(&b, false).unwrap());
2620 assert!(a.less_than(&b, true).unwrap());
2621 assert!(!b.less_than(&a, false).unwrap());
2622 assert!(a.less_than(&a, true).unwrap()); assert!(!a.less_than(&a, false).unwrap()); }
2625
2626 #[test]
2627 fn less_than_with_u128_values() {
2628 let a = WhereClause {
2629 field: "f".to_string(),
2630 operator: Equal,
2631 value: Value::U128(1),
2632 };
2633 let b = WhereClause {
2634 field: "f".to_string(),
2635 operator: Equal,
2636 value: Value::U128(2),
2637 };
2638 assert!(a.less_than(&b, false).unwrap());
2639 assert!(!b.less_than(&a, false).unwrap());
2640 }
2641
2642 #[test]
2643 fn less_than_with_i64_values() {
2644 let a = WhereClause {
2645 field: "f".to_string(),
2646 operator: Equal,
2647 value: Value::I64(-5),
2648 };
2649 let b = WhereClause {
2650 field: "f".to_string(),
2651 operator: Equal,
2652 value: Value::I64(10),
2653 };
2654 assert!(a.less_than(&b, false).unwrap());
2655 }
2656
2657 #[test]
2658 fn less_than_with_u64_values() {
2659 let a = WhereClause {
2660 field: "f".to_string(),
2661 operator: Equal,
2662 value: Value::U64(3),
2663 };
2664 let b = WhereClause {
2665 field: "f".to_string(),
2666 operator: Equal,
2667 value: Value::U64(7),
2668 };
2669 assert!(a.less_than(&b, false).unwrap());
2670 }
2671
2672 #[test]
2673 fn less_than_with_i32_values() {
2674 let a = WhereClause {
2675 field: "f".to_string(),
2676 operator: Equal,
2677 value: Value::I32(1),
2678 };
2679 let b = WhereClause {
2680 field: "f".to_string(),
2681 operator: Equal,
2682 value: Value::I32(2),
2683 };
2684 assert!(a.less_than(&b, false).unwrap());
2685 }
2686
2687 #[test]
2688 fn less_than_with_u32_values() {
2689 let a = WhereClause {
2690 field: "f".to_string(),
2691 operator: Equal,
2692 value: Value::U32(1),
2693 };
2694 let b = WhereClause {
2695 field: "f".to_string(),
2696 operator: Equal,
2697 value: Value::U32(2),
2698 };
2699 assert!(a.less_than(&b, false).unwrap());
2700 }
2701
2702 #[test]
2703 fn less_than_with_i16_values() {
2704 let a = WhereClause {
2705 field: "f".to_string(),
2706 operator: Equal,
2707 value: Value::I16(1),
2708 };
2709 let b = WhereClause {
2710 field: "f".to_string(),
2711 operator: Equal,
2712 value: Value::I16(2),
2713 };
2714 assert!(a.less_than(&b, false).unwrap());
2715 assert!(a.less_than(&b, true).unwrap());
2716 }
2717
2718 #[test]
2719 fn less_than_with_u16_values() {
2720 let a = WhereClause {
2721 field: "f".to_string(),
2722 operator: Equal,
2723 value: Value::U16(1),
2724 };
2725 let b = WhereClause {
2726 field: "f".to_string(),
2727 operator: Equal,
2728 value: Value::U16(2),
2729 };
2730 assert!(a.less_than(&b, false).unwrap());
2731 }
2732
2733 #[test]
2734 fn less_than_with_i8_values() {
2735 let a = WhereClause {
2736 field: "f".to_string(),
2737 operator: Equal,
2738 value: Value::I8(1),
2739 };
2740 let b = WhereClause {
2741 field: "f".to_string(),
2742 operator: Equal,
2743 value: Value::I8(2),
2744 };
2745 assert!(a.less_than(&b, false).unwrap());
2746 }
2747
2748 #[test]
2749 fn less_than_with_u8_values() {
2750 let a = WhereClause {
2751 field: "f".to_string(),
2752 operator: Equal,
2753 value: Value::U8(1),
2754 };
2755 let b = WhereClause {
2756 field: "f".to_string(),
2757 operator: Equal,
2758 value: Value::U8(2),
2759 };
2760 assert!(a.less_than(&b, false).unwrap());
2761 }
2762
2763 #[test]
2764 fn less_than_with_bytes_values() {
2765 let a = WhereClause {
2766 field: "f".to_string(),
2767 operator: Equal,
2768 value: Value::Bytes(vec![1, 2]),
2769 };
2770 let b = WhereClause {
2771 field: "f".to_string(),
2772 operator: Equal,
2773 value: Value::Bytes(vec![1, 3]),
2774 };
2775 assert!(a.less_than(&b, false).unwrap());
2776 }
2777
2778 #[test]
2779 fn less_than_with_float_values() {
2780 let a = WhereClause {
2781 field: "f".to_string(),
2782 operator: Equal,
2783 value: Value::Float(1.5),
2784 };
2785 let b = WhereClause {
2786 field: "f".to_string(),
2787 operator: Equal,
2788 value: Value::Float(2.5),
2789 };
2790 assert!(a.less_than(&b, false).unwrap());
2791 assert!(a.less_than(&b, true).unwrap());
2792 }
2793
2794 #[test]
2795 fn less_than_with_text_values() {
2796 let a = WhereClause {
2797 field: "f".to_string(),
2798 operator: Equal,
2799 value: Value::Text("abc".to_string()),
2800 };
2801 let b = WhereClause {
2802 field: "f".to_string(),
2803 operator: Equal,
2804 value: Value::Text("xyz".to_string()),
2805 };
2806 assert!(a.less_than(&b, false).unwrap());
2807 }
2808
2809 #[test]
2810 fn less_than_with_mismatched_types_returns_error() {
2811 let a = WhereClause {
2812 field: "f".to_string(),
2813 operator: Equal,
2814 value: Value::I64(1),
2815 };
2816 let b = WhereClause {
2817 field: "f".to_string(),
2818 operator: Equal,
2819 value: Value::Text("abc".to_string()),
2820 };
2821 assert!(a.less_than(&b, false).is_err());
2822 }
2823
2824 #[test]
2827 fn from_components_valid_clause() {
2828 let components = vec![
2829 Value::Text("name".to_string()),
2830 Value::Text("=".to_string()),
2831 Value::Text("alice".to_string()),
2832 ];
2833 let clause = WhereClause::from_components(&components).unwrap();
2834 assert_eq!(clause.field, "name");
2835 assert_eq!(clause.operator, Equal);
2836 assert_eq!(clause.value, Value::Text("alice".to_string()));
2837 }
2838
2839 #[test]
2840 fn from_components_wrong_count_returns_error() {
2841 let components = vec![
2842 Value::Text("name".to_string()),
2843 Value::Text("=".to_string()),
2844 ];
2845 assert!(WhereClause::from_components(&components).is_err());
2846
2847 let components = vec![
2848 Value::Text("name".to_string()),
2849 Value::Text("=".to_string()),
2850 Value::I64(1),
2851 Value::I64(2),
2852 ];
2853 assert!(WhereClause::from_components(&components).is_err());
2854 }
2855
2856 #[test]
2857 fn from_components_non_string_field_returns_error() {
2858 let components = vec![Value::I64(123), Value::Text("=".to_string()), Value::I64(1)];
2859 assert!(WhereClause::from_components(&components).is_err());
2860 }
2861
2862 #[test]
2863 fn from_components_non_string_operator_returns_error() {
2864 let components = vec![
2865 Value::Text("name".to_string()),
2866 Value::I64(1),
2867 Value::I64(1),
2868 ];
2869 assert!(WhereClause::from_components(&components).is_err());
2870 }
2871
2872 #[test]
2873 fn from_components_unknown_operator_returns_error() {
2874 let components = vec![
2875 Value::Text("name".to_string()),
2876 Value::Text("LIKE".to_string()),
2877 Value::I64(1),
2878 ];
2879 assert!(WhereClause::from_components(&components).is_err());
2880 }
2881
2882 #[test]
2883 fn from_components_with_in_operator() {
2884 let components = vec![
2885 Value::Text("status".to_string()),
2886 Value::Text("in".to_string()),
2887 Value::Array(vec![Value::I64(1), Value::I64(2)]),
2888 ];
2889 let clause = WhereClause::from_components(&components).unwrap();
2890 assert_eq!(clause.operator, In);
2891 }
2892
2893 #[test]
2894 fn from_components_with_starts_with_operator() {
2895 let components = vec![
2896 Value::Text("name".to_string()),
2897 Value::Text("startsWith".to_string()),
2898 Value::Text("alice".to_string()),
2899 ];
2900 let clause = WhereClause::from_components(&components).unwrap();
2901 assert_eq!(clause.operator, super::StartsWith);
2902 }
2903
2904 #[test]
2907 fn where_clause_into_value() {
2908 let clause = WhereClause {
2909 field: "name".to_string(),
2910 operator: Equal,
2911 value: Value::Text("alice".to_string()),
2912 };
2913 let val: Value = clause.into();
2914 match val {
2915 Value::Array(arr) => {
2916 assert_eq!(arr.len(), 3);
2917 assert_eq!(arr[0], Value::Text("name".to_string()));
2918 assert_eq!(arr[1], Value::Text("=".to_string()));
2919 assert_eq!(arr[2], Value::Text("alice".to_string()));
2920 }
2921 _ => panic!("expected Array"),
2922 }
2923 }
2924
2925 #[test]
2928 fn value_clause_matches_value_equal() {
2929 let clause = ValueClause {
2930 operator: Equal,
2931 value: Value::I64(42),
2932 };
2933 assert!(clause.matches_value(&Value::I64(42)));
2934 assert!(!clause.matches_value(&Value::I64(43)));
2935 }
2936
2937 #[test]
2938 fn value_clause_matches_value_greater_than() {
2939 let clause = ValueClause {
2940 operator: GreaterThan,
2941 value: Value::I64(10),
2942 };
2943 assert!(clause.matches_value(&Value::I64(20)));
2944 assert!(!clause.matches_value(&Value::I64(5)));
2945 }
2946
2947 #[test]
2948 fn value_clause_matches_value_in() {
2949 let clause = ValueClause {
2950 operator: In,
2951 value: Value::Array(vec![Value::I64(1), Value::I64(2), Value::I64(3)]),
2952 };
2953 assert!(clause.matches_value(&Value::I64(2)));
2954 assert!(!clause.matches_value(&Value::I64(4)));
2955 }
2956
2957 #[test]
2958 fn value_clause_matches_value_starts_with() {
2959 let clause = ValueClause {
2960 operator: super::StartsWith,
2961 value: Value::Text("hello".to_string()),
2962 };
2963 assert!(clause.matches_value(&Value::Text("hello world".to_string())));
2964 assert!(!clause.matches_value(&Value::Text("world hello".to_string())));
2965 }
2966
2967 #[test]
2970 fn where_clause_matches_value_delegates_to_eval() {
2971 let clause = WhereClause {
2972 field: "age".to_string(),
2973 operator: GreaterThanOrEquals,
2974 value: Value::I64(18),
2975 };
2976 assert!(clause.matches_value(&Value::I64(18)));
2977 assert!(clause.matches_value(&Value::I64(25)));
2978 assert!(!clause.matches_value(&Value::I64(17)));
2979 }
2980
2981 #[test]
2984 fn group_clauses_empty_input() {
2985 let clauses: Vec<WhereClause> = vec![];
2986 let (eq, range, in_c) = WhereClause::group_clauses(&clauses).expect("empty should succeed");
2987 assert!(eq.is_empty());
2988 assert!(range.is_none());
2989 assert!(in_c.is_none());
2990 }
2991
2992 #[test]
2993 fn group_clauses_single_equality() {
2994 let clauses = vec![WhereClause {
2995 field: "name".to_string(),
2996 operator: Equal,
2997 value: Value::Text("alice".to_string()),
2998 }];
2999 let (eq, range, in_c) = WhereClause::group_clauses(&clauses).unwrap();
3000 assert_eq!(eq.len(), 1);
3001 assert!(eq.contains_key("name"));
3002 assert!(range.is_none());
3003 assert!(in_c.is_none());
3004 }
3005
3006 #[test]
3007 fn group_clauses_equality_on_id_is_excluded_from_equals() {
3008 let clauses = vec![WhereClause {
3009 field: "$id".to_string(),
3010 operator: Equal,
3011 value: Value::I64(1),
3012 }];
3013 let (eq, range, in_c) = WhereClause::group_clauses(&clauses).unwrap();
3014 assert!(eq.is_empty());
3016 assert!(range.is_none());
3017 assert!(in_c.is_none());
3018 }
3019
3020 #[test]
3021 fn group_clauses_in_on_id_is_excluded_from_in_clause() {
3022 let clauses = vec![WhereClause {
3023 field: "$id".to_string(),
3024 operator: In,
3025 value: Value::Array(vec![Value::I64(1), Value::I64(2)]),
3026 }];
3027 let (eq, range, in_c) = WhereClause::group_clauses(&clauses).unwrap();
3028 assert!(eq.is_empty());
3029 assert!(range.is_none());
3030 assert!(in_c.is_none());
3031 }
3032
3033 #[test]
3034 fn group_clauses_single_in() {
3035 let clauses = vec![WhereClause {
3036 field: "status".to_string(),
3037 operator: In,
3038 value: Value::Array(vec![Value::I64(1), Value::I64(2)]),
3039 }];
3040 let (eq, range, in_c) = WhereClause::group_clauses(&clauses).unwrap();
3041 assert!(eq.is_empty());
3042 assert!(range.is_none());
3043 assert!(in_c.is_some());
3044 assert_eq!(in_c.unwrap().field, "status");
3045 }
3046
3047 #[test]
3048 fn group_clauses_multiple_in_returns_error() {
3049 let clauses = vec![
3050 WhereClause {
3051 field: "a".to_string(),
3052 operator: In,
3053 value: Value::Array(vec![Value::I64(1)]),
3054 },
3055 WhereClause {
3056 field: "b".to_string(),
3057 operator: In,
3058 value: Value::Array(vec![Value::I64(2)]),
3059 },
3060 ];
3061 assert!(WhereClause::group_clauses(&clauses).is_err());
3062 }
3063
3064 #[test]
3065 fn group_clauses_in_same_field_as_equality_returns_error() {
3066 let clauses = vec![
3067 WhereClause {
3068 field: "status".to_string(),
3069 operator: Equal,
3070 value: Value::I64(1),
3071 },
3072 WhereClause {
3073 field: "status".to_string(),
3074 operator: In,
3075 value: Value::Array(vec![Value::I64(2)]),
3076 },
3077 ];
3078 assert!(WhereClause::group_clauses(&clauses).is_err());
3079 }
3080
3081 #[test]
3082 fn group_clauses_duplicate_equality_same_field_returns_error() {
3083 let clauses = vec![
3084 WhereClause {
3085 field: "name".to_string(),
3086 operator: Equal,
3087 value: Value::Text("alice".to_string()),
3088 },
3089 WhereClause {
3090 field: "name".to_string(),
3091 operator: Equal,
3092 value: Value::Text("bob".to_string()),
3093 },
3094 ];
3095 assert!(WhereClause::group_clauses(&clauses).is_err());
3096 }
3097
3098 #[test]
3099 fn group_clauses_single_range_operator() {
3100 let clauses = vec![WhereClause {
3101 field: "age".to_string(),
3102 operator: GreaterThan,
3103 value: Value::I64(18),
3104 }];
3105 let (eq, range, in_c) = WhereClause::group_clauses(&clauses).unwrap();
3106 assert!(eq.is_empty());
3107 assert!(range.is_some());
3108 assert_eq!(range.unwrap().operator, GreaterThan);
3109 assert!(in_c.is_none());
3110 }
3111
3112 #[test]
3113 fn group_clauses_single_non_groupable_range_between() {
3114 let clauses = vec![WhereClause {
3115 field: "age".to_string(),
3116 operator: Between,
3117 value: Value::Array(vec![Value::Float(0.0), Value::Float(100.0)]),
3118 }];
3119 let (eq, range, in_c) = WhereClause::group_clauses(&clauses).unwrap();
3120 assert!(eq.is_empty());
3121 assert!(range.is_some());
3122 assert_eq!(range.unwrap().operator, Between);
3123 assert!(in_c.is_none());
3124 }
3125
3126 #[test]
3127 fn group_clauses_starts_with_empty_string_returns_error() {
3128 let clauses = vec![WhereClause {
3129 field: "name".to_string(),
3130 operator: super::StartsWith,
3131 value: Value::Text("".to_string()),
3132 }];
3133 assert!(WhereClause::group_clauses(&clauses).is_err());
3134 }
3135
3136 #[test]
3137 fn group_clauses_starts_with_valid_string() {
3138 let clauses = vec![WhereClause {
3139 field: "name".to_string(),
3140 operator: super::StartsWith,
3141 value: Value::Text("al".to_string()),
3142 }];
3143 let (eq, range, in_c) = WhereClause::group_clauses(&clauses).unwrap();
3144 assert!(eq.is_empty());
3145 assert!(range.is_some());
3146 assert_eq!(range.unwrap().operator, super::StartsWith);
3147 assert!(in_c.is_none());
3148 }
3149
3150 #[test]
3151 fn group_clauses_non_groupable_range_same_field_as_equality_returns_error() {
3152 let clauses = vec![
3153 WhereClause {
3154 field: "name".to_string(),
3155 operator: Equal,
3156 value: Value::Text("alice".to_string()),
3157 },
3158 WhereClause {
3159 field: "name".to_string(),
3160 operator: super::StartsWith,
3161 value: Value::Text("al".to_string()),
3162 },
3163 ];
3164 assert!(WhereClause::group_clauses(&clauses).is_err());
3165 }
3166
3167 #[test]
3168 fn group_clauses_multiple_non_groupable_ranges_returns_error() {
3169 let clauses = vec![
3170 WhereClause {
3171 field: "a".to_string(),
3172 operator: Between,
3173 value: Value::Array(vec![Value::Float(0.0), Value::Float(10.0)]),
3174 },
3175 WhereClause {
3176 field: "b".to_string(),
3177 operator: super::StartsWith,
3178 value: Value::Text("x".to_string()),
3179 },
3180 ];
3181 assert!(WhereClause::group_clauses(&clauses).is_err());
3182 }
3183
3184 #[test]
3185 fn group_clauses_mixed_groupable_and_non_groupable_returns_error() {
3186 let clauses = vec![
3187 WhereClause {
3188 field: "a".to_string(),
3189 operator: GreaterThan,
3190 value: Value::Float(0.0),
3191 },
3192 WhereClause {
3193 field: "b".to_string(),
3194 operator: Between,
3195 value: Value::Array(vec![Value::Float(0.0), Value::Float(10.0)]),
3196 },
3197 ];
3198 assert!(WhereClause::group_clauses(&clauses).is_err());
3199 }
3200
3201 #[test]
3202 fn group_clauses_three_groupable_ranges_returns_error() {
3203 let clauses = vec![
3204 WhereClause {
3205 field: "a".to_string(),
3206 operator: GreaterThan,
3207 value: Value::Float(0.0),
3208 },
3209 WhereClause {
3210 field: "a".to_string(),
3211 operator: LessThan,
3212 value: Value::Float(10.0),
3213 },
3214 WhereClause {
3215 field: "a".to_string(),
3216 operator: GreaterThanOrEquals,
3217 value: Value::Float(5.0),
3218 },
3219 ];
3220 assert!(WhereClause::group_clauses(&clauses).is_err());
3221 }
3222
3223 #[test]
3224 fn group_clauses_range_same_field_as_equality_returns_error() {
3225 let clauses = vec![
3226 WhereClause {
3227 field: "age".to_string(),
3228 operator: Equal,
3229 value: Value::I64(25),
3230 },
3231 WhereClause {
3232 field: "age".to_string(),
3233 operator: GreaterThan,
3234 value: Value::I64(18),
3235 },
3236 ];
3237 assert!(WhereClause::group_clauses(&clauses).is_err());
3238 }
3239
3240 #[test]
3241 fn group_clauses_two_ranges_combined_into_between() {
3242 let clauses = vec![
3243 WhereClause {
3244 field: "age".to_string(),
3245 operator: GreaterThanOrEquals,
3246 value: Value::Float(10.0),
3247 },
3248 WhereClause {
3249 field: "age".to_string(),
3250 operator: LessThanOrEquals,
3251 value: Value::Float(20.0),
3252 },
3253 ];
3254 let (_, range, _) = WhereClause::group_clauses(&clauses).unwrap();
3255 let r = range.unwrap();
3256 assert_eq!(r.operator, Between);
3257 assert_eq!(r.field, "age");
3258 }
3259
3260 #[test]
3261 fn group_clauses_two_ranges_combined_into_between_exclude_right() {
3262 let clauses = vec![
3263 WhereClause {
3264 field: "age".to_string(),
3265 operator: GreaterThanOrEquals,
3266 value: Value::Float(10.0),
3267 },
3268 WhereClause {
3269 field: "age".to_string(),
3270 operator: LessThan,
3271 value: Value::Float(20.0),
3272 },
3273 ];
3274 let (_, range, _) = WhereClause::group_clauses(&clauses).unwrap();
3275 assert_eq!(range.unwrap().operator, BetweenExcludeRight);
3276 }
3277
3278 #[test]
3279 fn group_clauses_two_ranges_combined_into_between_exclude_left() {
3280 let clauses = vec![
3281 WhereClause {
3282 field: "age".to_string(),
3283 operator: GreaterThan,
3284 value: Value::Float(10.0),
3285 },
3286 WhereClause {
3287 field: "age".to_string(),
3288 operator: LessThanOrEquals,
3289 value: Value::Float(20.0),
3290 },
3291 ];
3292 let (_, range, _) = WhereClause::group_clauses(&clauses).unwrap();
3293 assert_eq!(range.unwrap().operator, BetweenExcludeLeft);
3294 }
3295
3296 #[test]
3297 fn group_clauses_two_ranges_combined_into_between_exclude_bounds() {
3298 let clauses = vec![
3299 WhereClause {
3300 field: "age".to_string(),
3301 operator: GreaterThan,
3302 value: Value::Float(10.0),
3303 },
3304 WhereClause {
3305 field: "age".to_string(),
3306 operator: LessThan,
3307 value: Value::Float(20.0),
3308 },
3309 ];
3310 let (_, range, _) = WhereClause::group_clauses(&clauses).unwrap();
3311 assert_eq!(range.unwrap().operator, BetweenExcludeBounds);
3312 }
3313
3314 #[test]
3315 fn group_clauses_equality_plus_in_on_different_fields() {
3316 let clauses = vec![
3317 WhereClause {
3318 field: "name".to_string(),
3319 operator: Equal,
3320 value: Value::Text("alice".to_string()),
3321 },
3322 WhereClause {
3323 field: "status".to_string(),
3324 operator: In,
3325 value: Value::Array(vec![Value::I64(1), Value::I64(2)]),
3326 },
3327 ];
3328 let (eq, _, in_c) = WhereClause::group_clauses(&clauses).unwrap();
3329 assert_eq!(eq.len(), 1);
3330 assert!(in_c.is_some());
3331 }
3332
3333 #[test]
3334 fn group_clauses_equality_plus_range_on_different_fields() {
3335 let clauses = vec![
3336 WhereClause {
3337 field: "name".to_string(),
3338 operator: Equal,
3339 value: Value::Text("alice".to_string()),
3340 },
3341 WhereClause {
3342 field: "age".to_string(),
3343 operator: GreaterThan,
3344 value: Value::Float(18.0),
3345 },
3346 ];
3347 let (eq, range, in_c) = WhereClause::group_clauses(&clauses).unwrap();
3348 assert_eq!(eq.len(), 1);
3349 assert!(range.is_some());
3350 assert!(in_c.is_none());
3351 }
3352
3353 #[test]
3356 fn meta_field_property_type_all_identifiers() {
3357 use super::meta_field_property_type;
3358 use dpp::data_contract::document_type::DocumentPropertyType;
3359
3360 for field in ["$id", "$ownerId", "$dataContractId", "$creatorId"] {
3361 let pt = meta_field_property_type(field);
3362 assert!(
3363 matches!(pt, Some(DocumentPropertyType::Identifier)),
3364 "expected Identifier for {field}"
3365 );
3366 }
3367 }
3368
3369 #[test]
3370 fn meta_field_property_type_dates() {
3371 use super::meta_field_property_type;
3372 use dpp::data_contract::document_type::DocumentPropertyType;
3373
3374 for field in ["$createdAt", "$updatedAt", "$transferredAt"] {
3375 let pt = meta_field_property_type(field);
3376 assert!(
3377 matches!(pt, Some(DocumentPropertyType::Date)),
3378 "expected Date for {field}"
3379 );
3380 }
3381 }
3382
3383 #[test]
3384 fn meta_field_property_type_block_heights() {
3385 use super::meta_field_property_type;
3386 use dpp::data_contract::document_type::DocumentPropertyType;
3387
3388 for field in [
3389 "$createdAtBlockHeight",
3390 "$updatedAtBlockHeight",
3391 "$transferredAtBlockHeight",
3392 ] {
3393 let pt = meta_field_property_type(field);
3394 assert!(
3395 matches!(pt, Some(DocumentPropertyType::U64)),
3396 "expected U64 for {field}"
3397 );
3398 }
3399 }
3400
3401 #[test]
3402 fn meta_field_property_type_core_block_heights() {
3403 use super::meta_field_property_type;
3404 use dpp::data_contract::document_type::DocumentPropertyType;
3405
3406 for field in [
3407 "$createdAtCoreBlockHeight",
3408 "$updatedAtCoreBlockHeight",
3409 "$transferredAtCoreBlockHeight",
3410 ] {
3411 let pt = meta_field_property_type(field);
3412 assert!(
3413 matches!(pt, Some(DocumentPropertyType::U32)),
3414 "expected U32 for {field}"
3415 );
3416 }
3417 }
3418
3419 #[test]
3420 fn meta_field_property_type_revision_and_protocol_version() {
3421 use super::meta_field_property_type;
3422 use dpp::data_contract::document_type::DocumentPropertyType;
3423
3424 assert!(matches!(
3425 meta_field_property_type("$revision"),
3426 Some(DocumentPropertyType::U64)
3427 ));
3428 assert!(matches!(
3429 meta_field_property_type("$protocolVersion"),
3430 Some(DocumentPropertyType::U64)
3431 ));
3432 }
3433
3434 #[test]
3435 fn meta_field_property_type_type_field() {
3436 use super::meta_field_property_type;
3437 use dpp::data_contract::document_type::DocumentPropertyType;
3438
3439 assert!(matches!(
3440 meta_field_property_type("$type"),
3441 Some(DocumentPropertyType::String(_))
3442 ));
3443 }
3444
3445 #[test]
3446 fn meta_field_property_type_unknown_returns_none() {
3447 use super::meta_field_property_type;
3448
3449 assert!(meta_field_property_type("unknown").is_none());
3450 assert!(meta_field_property_type("$nonexistent").is_none());
3451 }
3452
3453 #[test]
3456 fn allowed_ops_for_numeric_types_include_ranges() {
3457 use super::allowed_ops_for_type;
3458 use dpp::data_contract::document_type::DocumentPropertyType;
3459
3460 for ty in [
3461 DocumentPropertyType::U8,
3462 DocumentPropertyType::I8,
3463 DocumentPropertyType::U16,
3464 DocumentPropertyType::I16,
3465 DocumentPropertyType::U32,
3466 DocumentPropertyType::I32,
3467 DocumentPropertyType::U64,
3468 DocumentPropertyType::I64,
3469 DocumentPropertyType::U128,
3470 DocumentPropertyType::I128,
3471 DocumentPropertyType::F64,
3472 DocumentPropertyType::Date,
3473 ] {
3474 let ops = allowed_ops_for_type(&ty);
3475 assert!(ops.contains(&Equal), "numeric type should allow Equal");
3476 assert!(ops.contains(&In), "numeric type should allow In");
3477 assert!(
3478 ops.contains(&GreaterThan),
3479 "numeric type should allow GreaterThan"
3480 );
3481 assert!(ops.contains(&Between), "numeric type should allow Between");
3482 assert!(
3483 !ops.contains(&super::StartsWith),
3484 "numeric type should not allow StartsWith"
3485 );
3486 }
3487 }
3488
3489 #[test]
3490 fn allowed_ops_for_string_includes_starts_with() {
3491 use super::allowed_ops_for_type;
3492 use dpp::data_contract::document_type::{DocumentPropertyType, StringPropertySizes};
3493
3494 let ty = DocumentPropertyType::String(StringPropertySizes {
3495 min_length: None,
3496 max_length: None,
3497 });
3498 let ops = allowed_ops_for_type(&ty);
3499 assert!(ops.contains(&super::StartsWith));
3500 assert!(ops.contains(&Equal));
3501 assert!(ops.contains(&In));
3502 assert!(ops.contains(&GreaterThan));
3503 }
3504
3505 #[test]
3506 fn allowed_ops_for_identifier_only_equal_and_in() {
3507 use super::allowed_ops_for_type;
3508 use dpp::data_contract::document_type::DocumentPropertyType;
3509
3510 let ops = allowed_ops_for_type(&DocumentPropertyType::Identifier);
3511 assert_eq!(ops, &[Equal, In]);
3512 }
3513
3514 #[test]
3515 fn allowed_ops_for_boolean_only_equal() {
3516 use super::allowed_ops_for_type;
3517 use dpp::data_contract::document_type::DocumentPropertyType;
3518
3519 let ops = allowed_ops_for_type(&DocumentPropertyType::Boolean);
3520 assert_eq!(ops, &[Equal]);
3521 }
3522
3523 #[test]
3524 fn allowed_ops_for_object_is_empty() {
3525 use super::allowed_ops_for_type;
3526 use dpp::data_contract::document_type::DocumentPropertyType;
3527
3528 let ops = allowed_ops_for_type(&DocumentPropertyType::Object(Default::default()));
3529 assert!(ops.is_empty());
3530 }
3531
3532 #[test]
3535 fn value_shape_ok_equal_always_true() {
3536 use super::WhereOperator;
3537 use dpp::data_contract::document_type::DocumentPropertyType;
3538
3539 assert!(WhereOperator::Equal.value_shape_ok(&Value::I64(1), &DocumentPropertyType::U64));
3541 assert!(WhereOperator::Equal
3542 .value_shape_ok(&Value::Text("x".into()), &DocumentPropertyType::Boolean));
3543 }
3544
3545 #[test]
3546 fn value_shape_ok_in_requires_array_or_bytes() {
3547 use super::WhereOperator;
3548 use dpp::data_contract::document_type::DocumentPropertyType;
3549
3550 assert!(WhereOperator::In.value_shape_ok(
3551 &Value::Array(vec![Value::I64(1)]),
3552 &DocumentPropertyType::U64
3553 ));
3554 assert!(WhereOperator::In.value_shape_ok(&Value::Bytes(vec![1]), &DocumentPropertyType::U8));
3555 assert!(!WhereOperator::In.value_shape_ok(&Value::I64(1), &DocumentPropertyType::U64));
3556 }
3557
3558 #[test]
3559 fn value_shape_ok_starts_with_requires_text() {
3560 use super::WhereOperator;
3561 use dpp::data_contract::document_type::{DocumentPropertyType, StringPropertySizes};
3562
3563 let str_ty = DocumentPropertyType::String(StringPropertySizes {
3564 min_length: None,
3565 max_length: None,
3566 });
3567 assert!(WhereOperator::StartsWith.value_shape_ok(&Value::Text("abc".into()), &str_ty));
3568 assert!(!WhereOperator::StartsWith.value_shape_ok(&Value::I64(1), &str_ty));
3569 }
3570
3571 #[test]
3572 fn value_shape_ok_range_for_f64_requires_numeric() {
3573 use super::WhereOperator;
3574 use dpp::data_contract::document_type::DocumentPropertyType;
3575
3576 assert!(WhereOperator::GreaterThan
3577 .value_shape_ok(&Value::Float(1.0), &DocumentPropertyType::F64));
3578 assert!(
3579 WhereOperator::GreaterThan.value_shape_ok(&Value::I64(1), &DocumentPropertyType::F64)
3580 );
3581 assert!(!WhereOperator::GreaterThan
3582 .value_shape_ok(&Value::Text("x".into()), &DocumentPropertyType::F64));
3583 }
3584
3585 #[test]
3586 fn value_shape_ok_range_for_string_requires_text() {
3587 use super::WhereOperator;
3588 use dpp::data_contract::document_type::{DocumentPropertyType, StringPropertySizes};
3589
3590 let str_ty = DocumentPropertyType::String(StringPropertySizes {
3591 min_length: None,
3592 max_length: None,
3593 });
3594 assert!(WhereOperator::LessThan.value_shape_ok(&Value::Text("a".into()), &str_ty));
3595 assert!(!WhereOperator::LessThan.value_shape_ok(&Value::I64(1), &str_ty));
3596 }
3597
3598 #[test]
3599 fn value_shape_ok_range_for_integer_requires_integer() {
3600 use super::WhereOperator;
3601 use dpp::data_contract::document_type::DocumentPropertyType;
3602
3603 assert!(
3604 WhereOperator::GreaterThan.value_shape_ok(&Value::U64(1), &DocumentPropertyType::U64)
3605 );
3606 assert!(
3607 WhereOperator::GreaterThan.value_shape_ok(&Value::I32(1), &DocumentPropertyType::I32)
3608 );
3609 assert!(!WhereOperator::GreaterThan
3610 .value_shape_ok(&Value::Float(1.0), &DocumentPropertyType::U64));
3611 assert!(!WhereOperator::GreaterThan
3612 .value_shape_ok(&Value::Text("x".into()), &DocumentPropertyType::U64));
3613 }
3614
3615 #[test]
3616 fn value_shape_ok_between_requires_array_of_two() {
3617 use super::WhereOperator;
3618 use dpp::data_contract::document_type::DocumentPropertyType;
3619
3620 let good = Value::Array(vec![Value::I64(1), Value::I64(10)]);
3621 assert!(WhereOperator::Between.value_shape_ok(&good, &DocumentPropertyType::I64));
3622
3623 let bad_len = Value::Array(vec![Value::I64(1)]);
3624 assert!(!WhereOperator::Between.value_shape_ok(&bad_len, &DocumentPropertyType::I64));
3625
3626 let not_array = Value::I64(5);
3627 assert!(!WhereOperator::Between.value_shape_ok(¬_array, &DocumentPropertyType::I64));
3628
3629 assert!(
3631 WhereOperator::BetweenExcludeBounds.value_shape_ok(&good, &DocumentPropertyType::I64)
3632 );
3633 assert!(WhereOperator::BetweenExcludeLeft.value_shape_ok(&good, &DocumentPropertyType::I64));
3634 assert!(
3635 WhereOperator::BetweenExcludeRight.value_shape_ok(&good, &DocumentPropertyType::I64)
3636 );
3637 }
3638
3639 #[test]
3642 fn validate_rejects_unknown_field() {
3643 let fixture = get_data_contract_fixture(None, 0, LATEST_PLATFORM_VERSION.protocol_version);
3644 let contract = fixture.data_contract_owned();
3645 let doc_type = contract
3646 .document_type_for_name("niceDocument")
3647 .expect("doc type exists");
3648
3649 let clause = WhereClause {
3650 field: "nonexistentField".to_string(),
3651 operator: Equal,
3652 value: Value::I64(1),
3653 };
3654 let res = clause.validate_against_schema(doc_type);
3655 assert!(res.is_err());
3656 }
3657
3658 #[test]
3659 fn validate_rejects_disallowed_operator_for_boolean() {
3660 let fixture = get_data_contract_fixture(None, 0, LATEST_PLATFORM_VERSION.protocol_version);
3661 let contract = fixture.data_contract_owned();
3662 let doc_type = contract
3663 .document_type_for_name("niceDocument")
3664 .expect("doc type exists");
3665
3666 let clause = WhereClause {
3670 field: "$type".to_string(),
3671 operator: super::StartsWith,
3672 value: Value::Text("nice".to_string()),
3673 };
3674 let res = clause.validate_against_schema(doc_type);
3675 assert!(res.is_valid());
3676 }
3677
3678 #[test]
3679 fn validate_rejects_starts_with_empty_string() {
3680 let fixture = get_data_contract_fixture(None, 0, LATEST_PLATFORM_VERSION.protocol_version);
3681 let contract = fixture.data_contract_owned();
3682 let doc_type = contract
3683 .document_type_for_name("niceDocument")
3684 .expect("doc type exists");
3685
3686 let clause = WhereClause {
3687 field: "$type".to_string(),
3688 operator: super::StartsWith,
3689 value: Value::Text("".to_string()),
3690 };
3691 let res = clause.validate_against_schema(doc_type);
3692 assert!(res.is_err());
3693 assert!(matches!(
3694 res.first_error(),
3695 Some(QuerySyntaxError::StartsWithIllegalString(_))
3696 ));
3697 }
3698
3699 #[test]
3700 fn validate_rejects_in_with_empty_array() {
3701 let fixture = get_data_contract_fixture(None, 0, LATEST_PLATFORM_VERSION.protocol_version);
3702 let contract = fixture.data_contract_owned();
3703 let doc_type = contract
3704 .document_type_for_name("niceDocument")
3705 .expect("doc type exists");
3706
3707 let clause = WhereClause {
3708 field: "$ownerId".to_string(),
3709 operator: In,
3710 value: Value::Array(vec![]),
3711 };
3712 let res = clause.validate_against_schema(doc_type);
3713 assert!(res.is_err());
3714 }
3715
3716 #[test]
3717 fn validate_rejects_in_with_duplicates() {
3718 let fixture = get_data_contract_fixture(None, 0, LATEST_PLATFORM_VERSION.protocol_version);
3719 let contract = fixture.data_contract_owned();
3720 let doc_type = contract
3721 .document_type_for_name("niceDocument")
3722 .expect("doc type exists");
3723
3724 let clause = WhereClause {
3725 field: "$ownerId".to_string(),
3726 operator: In,
3727 value: Value::Array(vec![
3728 Value::Identifier([1u8; 32]),
3729 Value::Identifier([1u8; 32]),
3730 ]),
3731 };
3732 let res = clause.validate_against_schema(doc_type);
3733 assert!(res.is_err());
3734 }
3735
3736 #[test]
3737 fn validate_rejects_between_with_descending_bounds() {
3738 let fixture = get_data_contract_fixture(None, 0, LATEST_PLATFORM_VERSION.protocol_version);
3739 let contract = fixture.data_contract_owned();
3740 let doc_type = contract
3741 .document_type_for_name("uniqueDates")
3742 .expect("doc type exists");
3743
3744 let clause = WhereClause {
3745 field: "$createdAt".to_string(),
3746 operator: Between,
3747 value: Value::Array(vec![Value::U64(2000), Value::U64(1000)]),
3748 };
3749 let res = clause.validate_against_schema(doc_type);
3750 assert!(res.is_err());
3751 assert!(matches!(
3752 res.first_error(),
3753 Some(QuerySyntaxError::InvalidBetweenClause(_))
3754 ));
3755 }
3756
3757 #[test]
3758 fn validate_rejects_range_operator_not_allowed_for_identifier() {
3759 let fixture = get_data_contract_fixture(None, 0, LATEST_PLATFORM_VERSION.protocol_version);
3760 let contract = fixture.data_contract_owned();
3761 let doc_type = contract
3762 .document_type_for_name("niceDocument")
3763 .expect("doc type exists");
3764
3765 let clause = WhereClause {
3766 field: "$ownerId".to_string(),
3767 operator: GreaterThan,
3768 value: Value::Identifier([1u8; 32]),
3769 };
3770 let res = clause.validate_against_schema(doc_type);
3771 assert!(res.is_err());
3772 }
3773
3774 #[test]
3775 fn validate_accepts_valid_integer_equality() {
3776 let fixture = get_data_contract_fixture(None, 0, LATEST_PLATFORM_VERSION.protocol_version);
3777 let contract = fixture.data_contract_owned();
3778 let doc_type = contract
3779 .document_type_for_name("niceDocument")
3780 .expect("doc type exists");
3781
3782 let clause = WhereClause {
3783 field: "$revision".to_string(),
3784 operator: Equal,
3785 value: Value::U64(5),
3786 };
3787 let res = clause.validate_against_schema(doc_type);
3788 assert!(res.is_valid());
3789 }
3790}