1use super::super::enums::{EnumComparisonState, EnumLabelCatalog};
10use super::{BinaryOp, Result, SQLError, Value};
11use std::cmp::Ordering;
12use uqa_core::memory::ProductionControl;
13
14mod records;
15
16#[cfg(test)]
17pub(in crate::expr) fn eval_comparison_op(op: BinaryOp, l: &Value, r: &Value) -> Result<Value> {
18 Ok(eval_comparison_truth(op, l, r)?
19 .map(Value::Bool)
20 .unwrap_or(Value::Null))
21}
22
23#[inline]
25pub fn eval_comparison_truth(op: BinaryOp, l: &Value, r: &Value) -> Result<Option<bool>> {
26 eval_comparison_truth_with_control(op, l, r, &ProductionControl::uncontrolled())
27}
28
29pub fn eval_comparison_truth_with_control(
31 op: BinaryOp,
32 l: &Value,
33 r: &Value,
34 control: &ProductionControl<'_>,
35) -> Result<Option<bool>> {
36 control.check()?;
37 let out = match op {
38 BinaryOp::Equal => values_equal_nullable_with_control(l, r, control)?,
39 BinaryOp::NotEqual => values_equal_nullable_with_control(l, r, control)?.map(|v| !v),
40 BinaryOp::Less => compare_nullable_with_control(l, r, control)?.map(|v| v.is_lt()),
41 BinaryOp::LessEqual => compare_nullable_with_control(l, r, control)?.map(|v| v.is_le()),
42 BinaryOp::Greater => compare_nullable_with_control(l, r, control)?.map(|v| v.is_gt()),
43 BinaryOp::GreaterEqual => compare_nullable_with_control(l, r, control)?.map(|v| v.is_ge()),
44 _ => {
45 return Err(SQLError::Internal(format!(
46 "non-comparison operator {op:?} reached comparison evaluation"
47 )))
48 }
49 };
50 Ok(out)
51}
52
53pub fn eval_comparison_truth_with_engine(
55 op: BinaryOp,
56 left: &Value,
57 right: &Value,
58 control: &ProductionControl<'_>,
59 engine: Option<&dyn crate::expr::EngineHook>,
60 state: Option<&EnumComparisonState>,
61) -> Result<Option<bool>> {
62 eval_comparison_truth_with_deferred_state(op, left, right, control, engine, || state)
63}
64
65#[inline]
67pub fn eval_comparison_truth_with_deferred_state<'a>(
68 op: BinaryOp,
69 left: &Value,
70 right: &Value,
71 control: &ProductionControl<'_>,
72 engine: Option<&dyn crate::expr::EngineHook>,
73 state: impl FnOnce() -> Option<&'a EnumComparisonState>,
74) -> Result<Option<bool>> {
75 control.check()?;
76 let primitive = match (left, right) {
77 (Value::Null, _) | (_, Value::Null) => Some(None),
78 (Value::Int(_), Value::Int(_))
79 | (Value::Float(_), Value::Float(_))
80 | (Value::Bool(_), Value::Bool(_)) => Some(Some(left.cmp(right))),
81 _ => None,
82 };
83 if let Some(order) = primitive {
84 return match op {
85 BinaryOp::Equal => Ok(order.map(Ordering::is_eq)),
86 BinaryOp::NotEqual => Ok(order.map(|order| !order.is_eq())),
87 BinaryOp::Less => Ok(order.map(Ordering::is_lt)),
88 BinaryOp::LessEqual => Ok(order.map(Ordering::is_le)),
89 BinaryOp::Greater => Ok(order.map(Ordering::is_gt)),
90 BinaryOp::GreaterEqual => Ok(order.map(Ordering::is_ge)),
91 _ => Err(SQLError::Internal(format!(
92 "non-comparison operator {op:?} reached comparison evaluation"
93 ))),
94 };
95 }
96 let catalog = engine.map(crate::expr::value_catalog::EngineValueCatalog);
97 eval_comparison_truth_with_enum_catalog(
98 op,
99 left,
100 right,
101 control,
102 catalog
103 .as_ref()
104 .map(|catalog| catalog as &dyn crate::expr::SQLValueCatalog),
105 state(),
106 )
107}
108
109pub fn eval_comparison_truth_with_enum_catalog(
111 op: BinaryOp,
112 left: &Value,
113 right: &Value,
114 control: &ProductionControl<'_>,
115 enums: Option<&dyn super::super::enums::EnumLabelCatalog>,
116 state: Option<&super::super::enums::EnumComparisonState>,
117) -> Result<Option<bool>> {
118 control.check()?;
119 if matches!(op, BinaryOp::Equal | BinaryOp::NotEqual) {
120 return values_equal_nullable_with_catalog(left, right, control, enums)
121 .map(|equal| equal.map(|equal| if op == BinaryOp::Equal { equal } else { !equal }));
122 }
123 if !matches!(
124 op,
125 BinaryOp::Less | BinaryOp::LessEqual | BinaryOp::Greater | BinaryOp::GreaterEqual
126 ) {
127 return Err(SQLError::Internal(format!(
128 "non-comparison operator {op:?} reached comparison evaluation"
129 )));
130 }
131 let order = compare_nullable_with_catalog(left, right, control, enums, state)?;
132 Ok(order.map(|order| match op {
133 BinaryOp::Less => order.is_lt(),
134 BinaryOp::LessEqual => order.is_le(),
135 BinaryOp::Greater => order.is_gt(),
136 BinaryOp::GreaterEqual => order.is_ge(),
137 _ => unreachable!("only comparison operators reach comparison evaluation"),
138 }))
139}
140
141#[cfg(test)]
143pub(in crate::expr) fn values_equal(a: &Value, b: &Value) -> Result<bool> {
144 Ok(values_equal_nullable(a, b)? == Some(true))
145}
146
147pub fn values_equal_with_control(
148 a: &Value,
149 b: &Value,
150 control: &ProductionControl<'_>,
151) -> Result<bool> {
152 Ok(values_equal_nullable_with_control(a, b, control)? == Some(true))
153}
154
155#[cfg(test)]
156pub(in crate::expr) fn values_equal_nullable(a: &Value, b: &Value) -> Result<Option<bool>> {
157 values_equal_nullable_with_control(a, b, &ProductionControl::uncontrolled())
158}
159
160pub fn values_equal_nullable_with_control(
161 a: &Value,
162 b: &Value,
163 control: &ProductionControl<'_>,
164) -> Result<Option<bool>> {
165 values_equal_nullable_with_catalog(a, b, control, None)
166}
167
168fn values_equal_nullable_with_catalog(
169 a: &Value,
170 b: &Value,
171 control: &ProductionControl<'_>,
172 enums: Option<&dyn super::super::enums::EnumLabelCatalog>,
173) -> Result<Option<bool>> {
174 control.check()?;
175 if let Some(value) = super::super::enums::eval_comparison(BinaryOp::Equal, a, b, enums, None)? {
176 return match value {
177 Value::Null => Ok(None),
178 Value::Bool(value) => Ok(Some(value)),
179 _ => unreachable!("equality returns boolean or NULL"),
180 };
181 }
182 if let Some(order) = super::super::datums::compare_jsonb_with_control(a, b, control)? {
183 return Ok(Some(order.is_eq()));
184 }
185 let equal = match (a, b) {
186 (Value::Null, _) | (_, Value::Null) => None,
187 (Value::Datum(datum), _) => {
188 return values_equal_nullable_with_catalog(
189 &*super::super::datums::read_with_value_catalog_and_control(datum, enums, control)?,
190 b,
191 control,
192 enums,
193 )
194 }
195 (_, Value::Datum(datum)) => {
196 return values_equal_nullable_with_catalog(
197 a,
198 &*super::super::datums::read_with_value_catalog_and_control(datum, enums, control)?,
199 control,
200 enums,
201 )
202 }
203 (Value::Temporal(x), Value::Str(y)) | (Value::Str(y), Value::Temporal(x)) => Some(
204 x.parse_same_kind_in_order_with_control(
205 y,
206 crate::expr::transaction_timestamp_or_clock(),
207 crate::expr::temporal_date_order(),
208 control,
209 )?
210 .is_some_and(|parsed| x.cmp(&parsed).is_eq()),
211 ),
212 (Value::FixedChar(x), Value::Str(y)) | (Value::Str(y), Value::FixedChar(x)) => {
213 Some(compare_fixed_text(x, y, control)?.is_eq())
214 }
215 (Value::Row(xs), Value::Row(ys)) => {
217 if xs.len() != ys.len() {
218 return Ok(Some(false));
219 }
220 let mut unknown = false;
221 for (x, y) in xs.iter().zip(ys) {
222 let equal = if matches!((x, y), (Value::Row(_), Value::Row(_))) {
223 Some(equal_values(x, y, control, enums, false)?)
224 } else {
225 values_equal_nullable_with_catalog(x, y, control, enums)?
226 };
227 match equal {
228 Some(false) => return Ok(Some(false)),
229 Some(true) => {}
230 None => unknown = true,
231 }
232 }
233 if unknown {
234 None
235 } else {
236 Some(true)
237 }
238 }
239 _ => Some(equal_values(a, b, control, enums, false)?),
240 };
241 Ok(equal)
242}
243
244pub fn compare_with_control(
246 a: &Value,
247 b: &Value,
248 control: &ProductionControl<'_>,
249) -> Result<Ordering> {
250 Ok(compare_nullable_with_control(a, b, control)?.unwrap_or(Ordering::Equal))
251}
252
253pub fn compare_nullable_with_control(
254 a: &Value,
255 b: &Value,
256 control: &ProductionControl<'_>,
257) -> Result<Option<Ordering>> {
258 compare_nullable_with_catalog(a, b, control, None, None)
259}
260
261fn compare_nullable_with_catalog(
262 a: &Value,
263 b: &Value,
264 control: &ProductionControl<'_>,
265 enums: Option<&dyn EnumLabelCatalog>,
266 state: Option<&EnumComparisonState>,
267) -> Result<Option<Ordering>> {
268 control.check()?;
269 if matches!(a, Value::Null) || matches!(b, Value::Null) {
270 return Ok(None);
271 }
272 if let Some(order) = super::super::enums::comparison_order(a, b, enums, state, false)? {
273 return Ok(Some(order));
274 }
275 if let Some(order) = super::super::datums::compare_jsonb_with_control(a, b, control)? {
276 return Ok(Some(order));
277 }
278 match (a, b) {
279 (Value::Null, _) | (_, Value::Null) => Ok(None),
280 (Value::Datum(datum), _) => compare_nullable_with_catalog(
281 &*super::super::datums::read_with_value_catalog_and_control(datum, enums, control)?,
282 b,
283 control,
284 enums,
285 state,
286 ),
287 (_, Value::Datum(datum)) => compare_nullable_with_catalog(
288 a,
289 &*super::super::datums::read_with_value_catalog_and_control(datum, enums, control)?,
290 control,
291 enums,
292 state,
293 ),
294 (
295 Value::Int(_) | Value::Float(_) | Value::Decimal(_),
296 Value::Int(_) | Value::Float(_) | Value::Decimal(_),
297 )
298 | (Value::Bool(_), Value::Decimal(_))
299 | (Value::Decimal(_), Value::Bool(_))
300 | (Value::Str(_), Value::Str(_))
301 | (Value::FixedChar(_), Value::FixedChar(_))
302 | (Value::JsonB(_), Value::JsonB(_))
303 | (Value::Temporal(_), Value::Temporal(_))
304 | (Value::Bool(_), Value::Bool(_))
305 | (Value::Array(_), Value::Array(_))
306 | (Value::LegacyVector(_), Value::LegacyVector(_))
307 | (Value::List(_), Value::List(_))
308 | (Value::Record(_), Value::Record(_) | Value::Row(_))
309 | (Value::Row(_), Value::Record(_))
310 | (Value::Enum(_), Value::Enum(_)) => Ok(Some(compare_sql_values(a, b, control, enums)?)),
311 (Value::FixedChar(x), Value::Str(y)) | (Value::Str(x), Value::FixedChar(y)) => {
312 Ok(Some(compare_fixed_text(x, y, control)?))
313 }
314 (Value::Temporal(x), Value::Str(y)) => x
315 .parse_same_kind_in_order_with_control(
316 y,
317 crate::expr::transaction_timestamp_or_clock(),
318 crate::expr::temporal_date_order(),
319 control,
320 )?
321 .map(|parsed| Some(x.cmp(&parsed)))
322 .ok_or_else(|| SQLError::TypeMismatch(format!("cannot compare {a:?} with {b:?}"))),
323 (Value::Str(x), Value::Temporal(y)) => y
324 .parse_same_kind_in_order_with_control(
325 x,
326 crate::expr::transaction_timestamp_or_clock(),
327 crate::expr::temporal_date_order(),
328 control,
329 )?
330 .map(|parsed| Some(parsed.cmp(y)))
331 .ok_or_else(|| SQLError::TypeMismatch(format!("cannot compare {a:?} with {b:?}"))),
332 (Value::Row(xs), Value::Row(ys)) => compare_anonymous_row(xs, ys, control, enums, state),
334 (lhs, rhs) => Err(SQLError::TypeMismatch(format!(
335 "cannot compare {lhs:?} with {rhs:?}"
336 ))),
337 }
338}
339
340fn compare_anonymous_row(
341 left: &[Value],
342 right: &[Value],
343 control: &ProductionControl<'_>,
344 enums: Option<&dyn EnumLabelCatalog>,
345 state: Option<&EnumComparisonState>,
346) -> Result<Option<Ordering>> {
347 for (index, (left, right)) in left.iter().zip(right).enumerate() {
348 let field =
349 if matches!(left, Value::Enum(_) | Value::Datum(_)) && !matches!(right, Value::Null) {
350 state.map(|state| state.field(index))
351 } else {
352 None
353 };
354 let order = if matches!((left, right), (Value::Row(_), Value::Row(_))) {
355 Some(compare_typed_values_with_catalog(
356 left, right, control, enums,
357 )?)
358 } else {
359 compare_nullable_with_catalog(left, right, control, enums, field.as_deref())?
360 };
361 match order {
362 Some(Ordering::Equal) => {}
363 other => return Ok(other),
364 }
365 }
366 Ok(Some(left.len().cmp(&right.len())))
367}
368
369fn compare_sql_values(
370 left: &Value,
371 right: &Value,
372 control: &ProductionControl<'_>,
373 enums: Option<&dyn EnumLabelCatalog>,
374) -> Result<Ordering> {
375 if matches!(
377 (left, right),
378 (Value::Float(_), Value::Int(_) | Value::Decimal(_))
379 | (Value::Int(_) | Value::Decimal(_), Value::Float(_))
380 ) {
381 let left =
382 super::super::cast_value_from_with_control(left, "double precision", None, control)?;
383 let right =
384 super::super::cast_value_from_with_control(right, "double precision", None, control)?;
385 return Ok(left.cmp(&right));
386 }
387 compare_typed_values_with_catalog(left, right, control, enums)
388}
389
390pub fn compare_typed_values_with_control(
392 left: &Value,
393 right: &Value,
394 control: &ProductionControl<'_>,
395) -> Result<Ordering> {
396 compare_typed_values_with_catalog(left, right, control, None)
397}
398
399pub fn compare_typed_values_with_enum_catalog(
401 left: &Value,
402 right: &Value,
403 control: &ProductionControl<'_>,
404 enums: Option<&dyn EnumLabelCatalog>,
405 state: Option<&EnumComparisonState>,
406) -> Result<Ordering> {
407 control.check()?;
408 if !matches!(left, Value::Null) && !matches!(right, Value::Null) {
409 if let Some(order) =
410 super::super::enums::comparison_order(left, right, enums, state, false)?
411 {
412 return Ok(order);
413 }
414 }
415 compare_typed_values_with_catalog(left, right, control, enums)
416}
417
418pub fn compare_grouping_values_with_enum_catalog(
420 left: &Value,
421 right: &Value,
422 control: &ProductionControl<'_>,
423 enums: Option<&dyn EnumLabelCatalog>,
424) -> Result<Ordering> {
425 compare_typed_values_for_purpose(left, right, control, enums, EnumKeyOrder::Identity)
426}
427
428#[derive(Clone, Copy)]
429enum EnumKeyOrder {
430 Declaration,
431 Identity,
432}
433
434fn enum_key_order(
435 left: &Value,
436 right: &Value,
437 enums: Option<&dyn EnumLabelCatalog>,
438 order: EnumKeyOrder,
439) -> Result<Option<Ordering>> {
440 if matches!(order, EnumKeyOrder::Identity) {
441 if let Some(enums) = enums {
442 if let (Some(left), Some(right)) = (
443 super::super::enums::comparison_identity(enums, left)?,
444 super::super::enums::comparison_identity(enums, right)?,
445 ) {
446 return Ok(Some(left.cmp(&right)));
447 }
448 }
449 return Ok(None);
450 }
451 super::super::enums::comparison_order(left, right, enums, None, true)
452}
453
454fn compare_typed_values_with_catalog(
455 left: &Value,
456 right: &Value,
457 control: &ProductionControl<'_>,
458 enums: Option<&dyn EnumLabelCatalog>,
459) -> Result<Ordering> {
460 compare_typed_values_for_purpose(left, right, control, enums, EnumKeyOrder::Declaration)
461}
462
463fn compare_typed_values_for_purpose(
464 left: &Value,
465 right: &Value,
466 control: &ProductionControl<'_>,
467 enums: Option<&dyn EnumLabelCatalog>,
468 enum_order: EnumKeyOrder,
469) -> Result<Ordering> {
470 control.check()?;
471 if let Some(order) = super::super::datums::compare_jsonb_with_control(left, right, control)? {
472 return Ok(order);
473 }
474 match (left, right) {
475 (Value::Null, Value::Null) => return Ok(Ordering::Equal),
476 (Value::Null, _) => return Ok(Ordering::Greater),
477 (_, Value::Null) => return Ok(Ordering::Less),
478 _ => {}
479 }
480 if let Some(order) = enum_key_order(left, right, enums, enum_order)? {
481 return Ok(order);
482 }
483 match (left, right) {
484 (Value::Datum(datum), _) => {
485 return compare_typed_values_for_purpose(
486 &*super::super::datums::read_with_value_catalog_and_control(datum, enums, control)?,
487 right,
488 control,
489 enums,
490 enum_order,
491 )
492 }
493 (_, Value::Datum(datum)) => {
494 return compare_typed_values_for_purpose(
495 left,
496 &*super::super::datums::read_with_value_catalog_and_control(datum, enums, control)?,
497 control,
498 enums,
499 enum_order,
500 )
501 }
502 (Value::Array(left), Value::Array(right)) => {
503 validate_array_element_types(left, right)?;
504 return left.cmp_by_with_control(right, control, |left, right, control| {
505 compare_typed_values_for_purpose(left, right, control, enums, enum_order)
506 });
507 }
508 (Value::Record(_) | Value::Row(_), Value::Record(_) | Value::Row(_)) => {
509 return records::compare(
510 left,
511 right,
512 enums,
513 control,
514 Ordering::Equal,
515 |left, right| {
516 let order =
517 compare_typed_values_for_purpose(left, right, control, enums, enum_order)?;
518 Ok((!order.is_eq()).then_some(order))
519 },
520 );
521 }
522 (Value::List(left), Value::List(right)) => {
523 return compare_sequence(left.iter(), right.iter(), control, enums, enum_order);
524 }
525 (Value::Enum(left), Value::Enum(right)) if left.type_oid() == right.type_oid() => {
527 return Ok(left.key().cmp(right.key()));
528 }
529 (Value::Enum(_), _) | (_, Value::Enum(_)) => {
530 return Err(SQLError::Internal(format!(
531 "enum comparison reached operands of different types: {} and {}",
532 comparison_operand_type(left),
533 comparison_operand_type(right)
534 )));
535 }
536 _ => {}
537 }
538 for value in [left, right] {
539 if let Value::LegacyVector(vector) = value {
540 validate_legacy_vector_comparison(vector)?;
541 }
542 }
543 left.cmp_with_control(right, control).map_err(Into::into)
544}
545
546fn comparison_operand_type(value: &Value) -> String {
547 match value {
548 Value::Enum(label) => format!("enum type OID {}", label.type_oid()),
549 other => super::super::diagnostics::value_type_name(other).to_owned(),
550 }
551}
552
553pub fn validate_legacy_vector_comparison(vector: &uqa_core::LegacyVectorValue) -> Result<()> {
555 if vector.kind() == uqa_core::LegacyVectorKind::Oid && !vector.has_vector_layout() {
556 return Err(SQLError::Routine {
557 sqlstate: "42804".into(),
558 message: "array is not a valid oidvector".into(),
559 });
560 }
561 Ok(())
562}
563
564pub fn type_comparison_can_fail(ty: &crate::ast::ColumnType) -> bool {
566 use crate::ast::ColumnType;
567 match ty {
568 ColumnType::OidVector
569 | ColumnType::Record
570 | ColumnType::Composite(_)
571 | ColumnType::Array(_) => true,
572 ColumnType::Domain { base: element, .. } => type_comparison_can_fail(element),
573 _ => false,
574 }
575}
576
577pub fn value_comparison_can_fail(value: &Value) -> bool {
579 match value {
580 Value::Datum(_) => true,
581 Value::LegacyVector(vector) => {
582 vector.kind() == uqa_core::LegacyVectorKind::Oid && !vector.has_vector_layout()
583 }
584 Value::Array(array) => {
585 array.element_type_oid().is_some()
586 || array.elements().iter().any(value_comparison_can_fail)
587 }
588 Value::Row(values) => values.iter().any(value_comparison_can_fail),
589 Value::List(values) => values.iter().any(value_comparison_can_fail),
590 Value::Record(fields) => {
591 fields.type_oid().is_some()
592 || fields
593 .iter()
594 .any(|(_, value)| value_comparison_can_fail(value))
595 }
596 _ => false,
597 }
598}
599
600pub fn equal_typed_values_with_enum_catalog(
603 left: &Value,
604 right: &Value,
605 control: &ProductionControl<'_>,
606 enums: Option<&dyn super::super::enums::EnumLabelCatalog>,
607) -> Result<bool> {
608 equal_values(left, right, control, enums, true)
609}
610
611fn equal_values(
612 left: &Value,
613 right: &Value,
614 control: &ProductionControl<'_>,
615 enums: Option<&dyn super::super::enums::EnumLabelCatalog>,
616 typed: bool,
617) -> Result<bool> {
618 control.check()?;
619 if let Some(order) = super::super::datums::compare_jsonb_with_control(left, right, control)? {
620 return Ok(order.is_eq());
621 }
622 if matches!(left, Value::Null) || matches!(right, Value::Null) {
623 return Ok(matches!((left, right), (Value::Null, Value::Null)));
624 }
625 if let Some(enums) = enums {
626 if let (Some(left), Some(right)) = (
627 super::super::enums::comparison_identity(enums, left)?,
628 super::super::enums::comparison_identity(enums, right)?,
629 ) {
630 return Ok(left == right);
631 }
632 }
633 match (left, right) {
634 (Value::Datum(datum), _) => equal_values(
635 &*super::super::datums::read_with_value_catalog_and_control(datum, enums, control)?,
636 right,
637 control,
638 enums,
639 typed,
640 ),
641 (_, Value::Datum(datum)) => equal_values(
642 left,
643 &*super::super::datums::read_with_value_catalog_and_control(datum, enums, control)?,
644 control,
645 enums,
646 typed,
647 ),
648 (Value::Array(left), Value::Array(right)) => {
649 validate_array_element_types(left, right)?;
650 left.eq_by_with_control(right, control, |left, right, control| {
651 equal_values(left, right, control, enums, typed)
652 })
653 }
654 (Value::Record(_) | Value::Row(_), Value::Record(_) | Value::Row(_)) => {
655 records::compare(left, right, enums, control, true, |left, right| {
656 Ok((!equal_values(left, right, control, enums, typed)?).then_some(false))
657 })
658 }
659 (Value::List(left), Value::List(right)) => {
660 equal_sequence(left.iter(), right.iter(), control, enums, typed)
661 }
662 _ if typed => Ok(compare_typed_values_with_control(left, right, control)?.is_eq()),
663 _ => Ok(compare_sql_values(left, right, control, enums)?.is_eq()),
664 }
665}
666
667fn validate_array_element_types(
668 left: &uqa_core::ArrayValue,
669 right: &uqa_core::ArrayValue,
670) -> Result<()> {
671 if matches!((left.element_type_oid(), right.element_type_oid()), (Some(left), Some(right)) if left != right)
672 {
673 return Err(SQLError::Routine {
674 sqlstate: "42804".into(),
675 message: "cannot compare arrays of different element types".into(),
676 });
677 }
678 Ok(())
679}
680
681fn equal_sequence<'a>(
682 mut left: impl Iterator<Item = &'a Value>,
683 mut right: impl Iterator<Item = &'a Value>,
684 control: &ProductionControl<'_>,
685 enums: Option<&dyn super::super::enums::EnumLabelCatalog>,
686 typed: bool,
687) -> Result<bool> {
688 loop {
689 control.check()?;
690 match (left.next(), right.next()) {
691 (Some(left), Some(right)) if equal_values(left, right, control, enums, typed)? => {}
692 (None, None) => return Ok(true),
693 _ => return Ok(false),
694 }
695 }
696}
697
698fn compare_sequence<'a>(
699 mut left: impl Iterator<Item = &'a Value>,
700 mut right: impl Iterator<Item = &'a Value>,
701 control: &ProductionControl<'_>,
702 enums: Option<&dyn EnumLabelCatalog>,
703 enum_order: EnumKeyOrder,
704) -> Result<Ordering> {
705 loop {
706 control.check()?;
707 let ordering = match (left.next(), right.next()) {
708 (Some(left), Some(right)) => {
709 compare_typed_values_for_purpose(left, right, control, enums, enum_order)?
710 }
711 (Some(_), None) => Ordering::Greater,
712 (None, Some(_)) => Ordering::Less,
713 (None, None) => return Ok(Ordering::Equal),
714 };
715 if !ordering.is_eq() {
716 return Ok(ordering);
717 }
718 }
719}
720
721fn compare_fixed_text(
722 left: &str,
723 right: &str,
724 control: &ProductionControl<'_>,
725) -> Result<Ordering> {
726 fn trim<'a>(text: &'a str, control: &ProductionControl<'_>) -> Result<&'a [u8]> {
727 let mut bytes = text.as_bytes();
728 let mut checked = 0;
729 while bytes.last() == Some(&b' ') {
730 if checked % 4096 == 0 {
731 control.check()?;
732 }
733 bytes = &bytes[..bytes.len() - 1];
734 checked += 1;
735 }
736 Ok(bytes)
737 }
738 let left = trim(left, control)?;
739 let right = trim(right, control)?;
740 for (left, right) in left.chunks(4096).zip(right.chunks(4096)) {
741 control.check()?;
742 let ordering = left.cmp(right);
743 if !ordering.is_eq() {
744 return Ok(ordering);
745 }
746 }
747 control.check()?;
748 Ok(left.len().cmp(&right.len()))
749}
750
751#[cfg(test)]
752mod tests;