diff --git a/extension/partiql-extension-ion/src/boxed_ion.rs b/extension/partiql-extension-ion/src/boxed_ion.rs index 8434fee6..fe00c720 100644 --- a/extension/partiql-extension-ion/src/boxed_ion.rs +++ b/extension/partiql-extension-ion/src/boxed_ion.rs @@ -233,7 +233,12 @@ impl<'a, const NULLS_EQUAL: bool, const NAN_EQUAL: bool> NullableEq { fn eq(&self, rhs: &Self) -> Value { let wrap = IonEqualityValue::<'a, { NULLS_EQUAL }, { NAN_EQUAL }, _>; - wrap(&self.0.doc).eq(&wrap(&rhs.0.doc)) + NullableEq::eq(&wrap(&self.0.doc), &wrap(&rhs.0.doc)) + } + #[inline(always)] + fn eqg(&self, rhs: &Self) -> Value { + let wrap = IonEqualityValue::<'_, true, { NAN_EQUAL }, _>; + NullableEq::eq(&wrap(self.0), &wrap(rhs.0)) } } @@ -574,6 +579,11 @@ impl<'a, const NULLS_EQUAL: bool, const NAN_EQUAL: bool> NullableEq _ => Value::Boolean(false), } } + #[inline(always)] + fn eqg(&self, rhs: &Self) -> Value { + let wrap = IonEqualityValue::<'_, true, { NAN_EQUAL }, _>; + NullableEq::eq(&wrap(self.0), &wrap(rhs.0)) + } } impl<'a, const NULLS_EQUAL: bool, const NAN_EQUAL: bool> NullableEq @@ -622,6 +632,11 @@ impl<'a, const NULLS_EQUAL: bool, const NAN_EQUAL: bool> NullableEq Value::Boolean(result) } + #[inline(always)] + fn eqg(&self, rhs: &Self) -> Value { + let wrap = IonEqualityValue::<'_, true, { NAN_EQUAL }, _>; + NullableEq::eq(&wrap(self.0), &wrap(rhs.0)) + } } impl<'a, const NULLS_EQUAL: bool, const NAN_EQUAL: bool> NullableEq @@ -632,9 +647,14 @@ impl<'a, const NULLS_EQUAL: bool, const NAN_EQUAL: bool> NullableEq let (l, r) = (self.0, other.0); let l = l.iter().map(wrap); let r = r.iter().map(wrap); - let res = l.zip(r).all(|(l, r)| l == r); + let res = l.zip(r).all(|(l, r)| l.eqg(&r) == Value::Boolean(true)); Value::Boolean(res) } + #[inline(always)] + fn eqg(&self, rhs: &Self) -> Value { + let wrap = IonEqualityValue::<'_, true, { NAN_EQUAL }, _>; + NullableEq::eq(&wrap(self.0), &wrap(rhs.0)) + } } impl<'a, const NULLS_EQUAL: bool, const NAN_EQUAL: bool> NullableEq @@ -645,11 +665,16 @@ impl<'a, const NULLS_EQUAL: bool, const NAN_EQUAL: bool> NullableEq let (l, r) = (self.0, other.0); let l = l.iter().map(|(s, elt)| (s, wrap(elt))); let r = r.iter().map(|(s, elt)| (s, wrap(elt))); - let res = l.zip(r).all(|((ls, lelt), (rs, relt))| { - ls == rs && NullableEq::eq(&lelt, &relt) == Value::Boolean(true) - }); + let res = l + .zip(r) + .all(|((ls, lelt), (rs, relt))| ls == rs && lelt.eqg(&relt) == Value::Boolean(true)); Value::Boolean(res) } + #[inline(always)] + fn eqg(&self, rhs: &Self) -> Value { + let wrap = IonEqualityValue::<'_, true, { NAN_EQUAL }, _>; + NullableEq::eq(&wrap(self.0), &wrap(rhs.0)) + } } /* diff --git a/partiql-value/src/bag.rs b/partiql-value/src/bag.rs index 29c4686c..259aa20f 100644 --- a/partiql-value/src/bag.rs +++ b/partiql-value/src/bag.rs @@ -199,12 +199,18 @@ impl NullableEq for (v1, v2) in li.zip(ri) { let wrap = EqualityValue::<{ NULLS_EQUAL }, { NAN_EQUAL }, Value>; - if NullableEq::eq(&wrap(v1), &wrap(v2)) != Value::Boolean(true) { + if NullableEq::eqg(&wrap(v1), &wrap(v2)) != Value::Boolean(true) { return Value::Boolean(false); } } Value::Boolean(true) } + + #[inline(always)] + fn eqg(&self, rhs: &Self) -> Value { + let wrap = EqualityValue::<'_, true, { NAN_EQUAL }, _>; + NullableEq::eq(&wrap(self.0), &wrap(rhs.0)) + } } impl PartialOrd for Bag { diff --git a/partiql-value/src/comparison.rs b/partiql-value/src/comparison.rs index f65b2e9a..e71e05a2 100644 --- a/partiql-value/src/comparison.rs +++ b/partiql-value/src/comparison.rs @@ -44,6 +44,20 @@ pub trait NullableEq { _ => Value::Missing, } } + + /// `PartiQL's `eqg` is used to compare the internals of Lists, Bags, and Tuples. + /// + /// > The eqg, unlike the =, returns true when a NULL is compared to a NULL or a MISSING + /// > to a MISSING + fn eqg(&self, rhs: &Self) -> Value; + + fn neqg(&self, rhs: &Self) -> Value { + let eqg_result = NullableEq::eqg(self, rhs); + match eqg_result { + Value::Boolean(_) | Value::Null => !eqg_result, + _ => Value::Missing, + } + } } /// A wrapper on [`T`] that specifies if missing and null values should be equal. @@ -101,6 +115,12 @@ impl NullableEq (_, _) => Value::from(self.0 == rhs.0), } } + + #[inline(always)] + fn eqg(&self, rhs: &Self) -> Value { + let wrap = EqualityValue::<'_, true, { NAN_EQUAL }, _>; + NullableEq::eq(&wrap(self.0), &wrap(rhs.0)) + } } // `Value` comparison with Missing and Null propagation diff --git a/partiql-value/src/list.rs b/partiql-value/src/list.rs index 0ef84d4e..f4c4ef62 100644 --- a/partiql-value/src/list.rs +++ b/partiql-value/src/list.rs @@ -190,12 +190,18 @@ impl NullableEq } for (v1, v2) in self.0.iter().zip(other.0.iter()) { let wrap = EqualityValue::<{ NULLS_EQUAL }, { NAN_EQUAL }, Value>; - if NullableEq::eq(&wrap(v1), &wrap(v2)) != Value::Boolean(true) { + if NullableEq::eqg(&wrap(v1), &wrap(v2)) != Value::Boolean(true) { return Value::Boolean(false); } } Value::Boolean(true) } + + #[inline(always)] + fn eqg(&self, rhs: &Self) -> Value { + let wrap = EqualityValue::<'_, true, { NAN_EQUAL }, _>; + NullableEq::eq(&wrap(self.0), &wrap(rhs.0)) + } } impl PartialOrd for List { diff --git a/partiql-value/src/tuple.rs b/partiql-value/src/tuple.rs index b155adc2..aa27ad24 100644 --- a/partiql-value/src/tuple.rs +++ b/partiql-value/src/tuple.rs @@ -231,12 +231,18 @@ impl NullableEq return Value::Boolean(false); } let wrap = EqualityValue::<{ NULLS_EQUAL }, { NAN_EQUAL }, Value>; - if NullableEq::eq(&wrap(lv), &wrap(rv)) != Value::Boolean(true) { + if NullableEq::eqg(&wrap(lv), &wrap(rv)) != Value::Boolean(true) { return Value::Boolean(false); } } Value::Boolean(true) } + + #[inline(always)] + fn eqg(&self, rhs: &Self) -> Value { + let wrap = EqualityValue::<'_, true, { NAN_EQUAL }, _>; + NullableEq::eq(&wrap(self.0), &wrap(rhs.0)) + } } impl PartialOrd for Tuple { diff --git a/partiql-value/src/variant.rs b/partiql-value/src/variant.rs index ae2666dc..f9d0afc8 100644 --- a/partiql-value/src/variant.rs +++ b/partiql-value/src/variant.rs @@ -213,6 +213,12 @@ impl NullableEq let res = lty == rty && lty.value_eq_param(l, r, NULLS_EQUAL, NAN_EQUAL); Value::Boolean(res) } + + #[inline(always)] + fn eqg(&self, rhs: &Self) -> Value { + let wrap = EqualityValue::<'_, true, { NAN_EQUAL }, _>; + NullableEq::eq(&wrap(self.0), &wrap(rhs.0)) + } } #[cfg(feature = "serde")]