vortex_array/aggregate_fn/fns/all_non_distinct/
mod.rs1mod bool;
5mod decimal;
6mod extension;
7mod filter;
8mod fixed_size_list;
9mod list;
10mod map;
11mod primitive;
12mod struct_;
13#[cfg(test)]
14mod tests;
15mod varbin;
16mod variant;
17
18use std::sync::LazyLock;
19
20use vortex_error::VortexResult;
21use vortex_error::vortex_bail;
22use vortex_error::vortex_err;
23use vortex_session::registry::CachedId;
24
25use self::bool::check_bool_identical;
26use self::decimal::check_decimal_identical;
27use self::extension::check_extension_identical;
28use self::filter::shared_validity_mask;
29use self::fixed_size_list::check_fixed_size_list_identical;
30use self::list::check_list_identical;
31use self::map::check_map_identical;
32use self::primitive::check_primitive_identical;
33use self::struct_::check_struct_identical;
34use self::varbin::check_varbinview_identical;
35use crate::ArrayRef;
36use crate::Canonical;
37use crate::Columnar;
38use crate::ExecutionCtx;
39use crate::IntoArray;
40use crate::aggregate_fn::Accumulator;
41use crate::aggregate_fn::AggregateArgs;
42use crate::aggregate_fn::AggregateFnId;
43use crate::aggregate_fn::AggregateFnVTable;
44use crate::aggregate_fn::DynAccumulator;
45use crate::aggregate_fn::EmptyOptions;
46use crate::aggregate_fn::fns::all_non_distinct::variant::check_variant_identical;
47use crate::arrays::StructArray;
48use crate::arrays::struct_::StructArrayExt;
49use crate::dtype::DType;
50use crate::dtype::FieldNames;
51use crate::dtype::Nullability;
52use crate::scalar::Scalar;
53use crate::validity::Validity;
54
55pub fn all_non_distinct(a: &ArrayRef, b: &ArrayRef, ctx: &mut ExecutionCtx) -> VortexResult<bool> {
65 if a.dtype() != b.dtype() {
66 vortex_bail!(
67 "all_non_distinct: dtype mismatch: {} vs {}",
68 a.dtype(),
69 b.dtype()
70 );
71 }
72
73 if a.len() != b.len() {
74 vortex_bail!(
75 "all_non_distinct: length mismatch: {} vs {}",
76 a.len(),
77 b.len()
78 );
79 }
80
81 if a.is_empty() {
82 return Ok(true);
83 }
84
85 let Some(shared_validity) = shared_validity_mask(a, b, ctx)? else {
86 return Ok(false);
87 };
88 if shared_validity.true_count() == 0 {
89 return Ok(true);
90 }
91
92 let validity = Validity::from_mask(shared_validity, a.dtype().nullability());
93 let batch = StructArray::try_new(NAMES.clone(), vec![a.clone(), b.clone()], a.len(), validity)?
94 .into_array();
95
96 let mut acc = Accumulator::try_new(AllNonDistinct, EmptyOptions, batch.dtype().clone())?;
97 acc.accumulate(&batch, ctx)?;
98 let result = acc.finish()?;
99
100 Ok(result.as_bool().value().unwrap_or(false))
101}
102
103static NAMES: LazyLock<FieldNames> = LazyLock::new(|| FieldNames::from(["lhs", "rhs"]));
104
105#[derive(Clone, Debug)]
116pub struct AllNonDistinct;
117
118pub struct AllNonDistinctPartial {
120 all_non_distinct: bool,
121}
122
123impl AggregateFnVTable for AllNonDistinct {
124 type Options = EmptyOptions;
125 type Partial = AllNonDistinctPartial;
126
127 fn id(&self) -> AggregateFnId {
128 static ID: CachedId = CachedId::new("vortex.all_non_distinct");
129 *ID
130 }
131
132 fn serialize(&self, _options: &Self::Options) -> VortexResult<Option<Vec<u8>>> {
133 vortex_bail!("AllNonDistinct is not yet serializable");
134 }
135
136 fn return_dtype(&self, _options: &Self::Options, input_dtype: &DType) -> Option<DType> {
137 match input_dtype {
138 DType::Struct(fields, _) if fields.nfields() == 2 => {
139 let lhs = fields.fields().next()?;
140 let rhs = fields.fields().nth(1)?;
141 (lhs == rhs).then(|| DType::Bool(Nullability::NonNullable))
142 }
143 _ => None,
144 }
145 }
146
147 fn partial_dtype(&self, options: &Self::Options, input_dtype: &DType) -> Option<DType> {
148 self.return_dtype(options, input_dtype)
149 }
150
151 fn empty_partial(
152 &self,
153 _args: AggregateArgs<'_, Self::Options>,
154 ) -> VortexResult<Self::Partial> {
155 Ok(AllNonDistinctPartial {
156 all_non_distinct: true,
157 })
158 }
159
160 fn partial_from_scalar(
161 &self,
162 _args: AggregateArgs<'_, Self::Options>,
163 scalar: &Scalar,
164 ) -> VortexResult<Self::Partial> {
165 Ok(AllNonDistinctPartial {
166 all_non_distinct: scalar.as_bool().value().unwrap_or(false),
167 })
168 }
169
170 fn merge_partials(
171 &self,
172 _args: AggregateArgs<'_, Self::Options>,
173 first: Self::Partial,
174 second: Self::Partial,
175 ) -> VortexResult<Self::Partial> {
176 Ok(AllNonDistinctPartial {
177 all_non_distinct: first.all_non_distinct && second.all_non_distinct,
178 })
179 }
180
181 fn to_scalar(
182 &self,
183 _args: AggregateArgs<'_, Self::Options>,
184 partial: &Self::Partial,
185 ) -> VortexResult<Scalar> {
186 Ok(Scalar::bool(
187 partial.all_non_distinct,
188 Nullability::NonNullable,
189 ))
190 }
191
192 #[inline]
193 fn is_saturated(
194 &self,
195 _args: AggregateArgs<'_, Self::Options>,
196 partial: &Self::Partial,
197 ) -> bool {
198 !partial.all_non_distinct
199 }
200
201 fn accumulate(
202 &self,
203 _args: AggregateArgs<'_, Self::Options>,
204 partial: &mut Self::Partial,
205 batch: &Columnar,
206 ctx: &mut ExecutionCtx,
207 ) -> VortexResult<()> {
208 if !partial.all_non_distinct {
209 return Ok(());
210 }
211
212 match batch {
213 Columnar::Constant(c) => {
214 let _ = c;
215 Ok(())
216 }
217 Columnar::Canonical(c) => {
218 let Canonical::Struct(s) = c else {
219 vortex_bail!(
220 "AllNonDistinct expects a Struct canonical, got {:?}",
221 c.dtype()
222 );
223 };
224
225 let struct_mask = s.validity()?.execute_mask(s.len(), ctx)?;
228 if struct_mask.true_count() == 0 {
229 return Ok(());
230 }
231
232 let lhs = s.unmasked_field(0);
233 let rhs = s.unmasked_field(1);
234
235 let (lhs, rhs) = if struct_mask.true_count() == s.len() {
237 (lhs.clone(), rhs.clone())
238 } else {
239 (lhs.filter(struct_mask.clone())?, rhs.filter(struct_mask)?)
240 };
241
242 let lhs_canonical = lhs.execute::<Canonical>(ctx)?;
243 let rhs_canonical = rhs.execute::<Canonical>(ctx)?;
244
245 partial.all_non_distinct =
246 check_canonical_identical(&lhs_canonical, &rhs_canonical, ctx)?;
247
248 Ok(())
249 }
250 }
251 }
252
253 fn finalize(
254 &self,
255 _args: AggregateArgs<'_, Self::Options>,
256 _partials: ArrayRef,
257 ) -> VortexResult<ArrayRef> {
258 vortex_bail!("AllNonDistinct does not support array finalization");
259 }
260
261 fn finalize_scalar(
262 &self,
263 _args: AggregateArgs<'_, Self::Options>,
264 partial: &Self::Partial,
265 ) -> VortexResult<Scalar> {
266 Ok(Scalar::bool(
267 partial.all_non_distinct,
268 Nullability::NonNullable,
269 ))
270 }
271}
272
273fn check_canonical_identical(
274 lhs: &Canonical,
275 rhs: &Canonical,
276 ctx: &mut ExecutionCtx,
277) -> VortexResult<bool> {
278 match (lhs, rhs) {
279 (Canonical::Null(_), Canonical::Null(_)) => Ok(true),
280 (Canonical::Bool(lhs), Canonical::Bool(rhs)) => check_bool_identical(lhs, rhs),
281 (Canonical::Primitive(lhs), Canonical::Primitive(rhs)) => {
282 check_primitive_identical(lhs, rhs)
283 }
284 (Canonical::Decimal(lhs), Canonical::Decimal(rhs)) => check_decimal_identical(lhs, rhs),
285 (Canonical::VarBinView(lhs), Canonical::VarBinView(rhs)) => {
286 check_varbinview_identical(lhs, rhs)
287 }
288 (Canonical::Struct(lhs), Canonical::Struct(rhs)) => check_struct_identical(lhs, rhs, ctx),
289 (Canonical::List(lhs), Canonical::List(rhs)) => check_list_identical(lhs, rhs, ctx),
290 (Canonical::Map(lhs), Canonical::Map(rhs)) => check_map_identical(lhs, rhs, ctx),
291 (Canonical::FixedSizeList(lhs), Canonical::FixedSizeList(rhs)) => {
292 check_fixed_size_list_identical(lhs, rhs, ctx)
293 }
294 (Canonical::Extension(lhs), Canonical::Extension(rhs)) => {
295 check_extension_identical(lhs, rhs, ctx)
296 }
297 (Canonical::Variant(lhs), Canonical::Variant(rhs)) => {
298 check_variant_identical(lhs, rhs, ctx)
299 }
300 _ => Err(vortex_err!(
301 "Canonical type mismatch in AllNonDistinct: {:?} vs {:?}",
302 lhs.dtype(),
303 rhs.dtype()
304 )),
305 }
306}