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