1use arrow::array::types::{IntervalDayTime, IntervalMonthDayNano};
21use arrow::array::*;
22#[cfg(not(feature = "force_hash_collisions"))]
23use arrow::compute::take;
24use arrow::datatypes::*;
25#[cfg(not(feature = "force_hash_collisions"))]
26use arrow::{downcast_dictionary_array, downcast_primitive_array};
27use foldhash::fast::FixedState;
28#[cfg(not(feature = "force_hash_collisions"))]
29use itertools::Itertools;
30#[cfg(not(feature = "force_hash_collisions"))]
31use std::collections::HashMap;
32use std::hash::{BuildHasher, Hash, Hasher};
33
34pub type RandomState = FixedState;
42pub type QualityRandomState = foldhash::quality::FixedState;
43
44pub const HLL_RANDOM_STATE: QualityRandomState = QualityRandomState::with_seed(0);
49
50pub trait HashState: BuildHasher {
56 type SeededState: BuildHasher;
57
58 fn seeded_state(&self, seed: u64) -> Self::SeededState;
59}
60
61impl HashState for FixedState {
62 type SeededState = foldhash::fast::SeedableRandomState;
63
64 fn seeded_state(&self, seed: u64) -> Self::SeededState {
65 foldhash::fast::SeedableRandomState::with_seed(
66 seed,
67 foldhash::SharedSeed::global_fixed(),
68 )
69 }
70}
71
72impl HashState for foldhash::quality::FixedState {
73 type SeededState = foldhash::quality::SeedableRandomState;
74
75 fn seeded_state(&self, seed: u64) -> Self::SeededState {
76 foldhash::quality::SeedableRandomState::with_seed(
77 seed,
78 foldhash::SharedSeed::global_fixed(),
79 )
80 }
81}
82
83#[cfg(not(feature = "force_hash_collisions"))]
84use crate::cast::{
85 as_binary_view_array, as_boolean_array, as_fixed_size_list_array,
86 as_generic_binary_array, as_large_list_array, as_large_list_view_array,
87 as_list_array, as_list_view_array, as_map_array, as_string_array,
88 as_string_view_array, as_struct_array, as_union_array,
89};
90use crate::error::Result;
91use crate::error::{_internal_datafusion_err, _internal_err};
92use std::cell::RefCell;
93
94mod build_hasher;
95
96#[inline]
98pub fn combine_hashes(l: u64, r: u64) -> u64 {
99 let hash = (17 * 37u64).wrapping_add(l);
100 hash.wrapping_mul(37).wrapping_add(r)
101}
102
103const MAX_BUFFER_SIZE: usize = 524_288;
108
109thread_local! {
110 static HASH_BUFFER: RefCell<Vec<u64>> = const { RefCell::new(Vec::new()) };
115}
116
117pub fn with_hashes<I, T, F, R>(
149 arrays: I,
150 random_state: &impl HashState,
151 callback: F,
152) -> Result<R>
153where
154 I: IntoIterator<Item = T>,
155 T: AsDynArray,
156 F: FnOnce(&[u64]) -> Result<R>,
157{
158 let mut iter = arrays.into_iter().peekable();
160
161 let required_size = match iter.peek() {
163 Some(arr) => arr.as_dyn_array().len(),
164 None => return _internal_err!("with_hashes requires at least one array"),
165 };
166
167 HASH_BUFFER.try_with(|cell| {
168 let mut buffer = cell.try_borrow_mut()
169 .map_err(|_| _internal_datafusion_err!("with_hashes cannot be called reentrantly on the same thread"))?;
170
171 buffer.clear();
173 buffer.resize(required_size, 0);
174
175 create_hashes(iter, random_state, &mut buffer[..required_size])?;
177
178 let result = callback(&buffer[..required_size])?;
180
181 if buffer.capacity() > MAX_BUFFER_SIZE {
183 buffer.truncate(MAX_BUFFER_SIZE);
184 buffer.shrink_to_fit();
185 }
186
187 Ok(result)
188 }).map_err(|_| _internal_datafusion_err!("with_hashes cannot access thread-local storage during or after thread destruction"))?
189}
190
191pub fn with_hashes_with_hasher<I, T, F, R, S>(
197 arrays: I,
198 hash_builder: &S,
199 callback: F,
200) -> Result<R>
201where
202 I: IntoIterator<Item = T>,
203 T: AsDynArray,
204 F: FnOnce(&[u64]) -> Result<R>,
205 S: BuildHasher,
206{
207 build_hasher::with_hashes_with_hasher(arrays, hash_builder, callback)
208}
209
210#[cfg(not(feature = "force_hash_collisions"))]
211fn hash_null<S: HashState>(
212 random_state: &S,
213 hashes_buffer: &'_ mut [u64],
214 multi_col: bool,
215) {
216 if multi_col {
217 hashes_buffer.iter_mut().for_each(|hash| {
218 *hash = combine_hashes(random_state.hash_one(1), *hash);
220 })
221 } else {
222 hashes_buffer.iter_mut().for_each(|hash| {
223 *hash = random_state.hash_one(1);
224 })
225 }
226}
227
228pub trait HashValue {
229 fn hash_one<S: BuildHasher>(&self, state: &S) -> u64;
230 fn hash_write(&self, hasher: &mut impl Hasher);
232}
233
234impl<T: HashValue + ?Sized> HashValue for &T {
235 fn hash_one<S: BuildHasher>(&self, state: &S) -> u64 {
236 T::hash_one(self, state)
237 }
238 fn hash_write(&self, hasher: &mut impl Hasher) {
239 T::hash_write(self, hasher)
240 }
241}
242
243macro_rules! hash_value {
244 ($($t:ty),+) => {
245 $(impl HashValue for $t {
246 fn hash_one<S: BuildHasher>(&self, state: &S) -> u64 {
247 state.hash_one(self)
248 }
249 fn hash_write(&self, hasher: &mut impl Hasher) {
250 Hash::hash(self, hasher)
251 }
252 })+
253 };
254}
255hash_value!(i8, i16, i32, i64, i128, i256, u8, u16, u32, u64, u128);
256hash_value!(bool, str, [u8], IntervalDayTime, IntervalMonthDayNano);
257
258macro_rules! hash_float_value {
259 ($(($t:ty, $i:ty)),+) => {
260 $(impl HashValue for $t {
261 fn hash_one<S: BuildHasher>(&self, state: &S) -> u64 {
262 let bits = <$i>::from_ne_bytes(self.to_ne_bytes());
265 let bits = if bits << 1 == 0 { 0 } else { bits };
266 state.hash_one(bits)
267 }
268 fn hash_write(&self, hasher: &mut impl Hasher) {
269 let bits = <$i>::from_ne_bytes(self.to_ne_bytes());
270 let bits: $i = if bits << 1 == 0 { 0 } else { bits };
271 hasher.write(&bits.to_ne_bytes())
272 }
273 })+
274 };
275}
276hash_float_value!((half::f16, u16), (f32, u32), (f64, u64));
277
278#[cfg(not(feature = "force_hash_collisions"))]
279trait ChildHashing {
280 fn create_hashes<I, T>(&self, arrays: I, hashes_buffer: &mut [u64]) -> Result<()>
281 where
282 I: IntoIterator<Item = T>,
283 T: AsDynArray;
284}
285
286#[cfg(not(feature = "force_hash_collisions"))]
287struct HashStateChildHashing<'a, S> {
288 hash_state: &'a S,
289}
290
291#[cfg(not(feature = "force_hash_collisions"))]
292impl<S: HashState> ChildHashing for HashStateChildHashing<'_, S> {
293 fn create_hashes<I, T>(&self, arrays: I, hashes_buffer: &mut [u64]) -> Result<()>
294 where
295 I: IntoIterator<Item = T>,
296 T: AsDynArray,
297 {
298 create_hashes(arrays, self.hash_state, hashes_buffer).map(|_| ())
299 }
300}
301
302#[cfg(not(feature = "force_hash_collisions"))]
306fn hash_array_primitive<T>(
307 array: &PrimitiveArray<T>,
308 random_state: &impl HashState,
309 hashes_buffer: &mut [u64],
310 rehash: bool,
311) where
312 T: ArrowPrimitiveType<Native: HashValue>,
313{
314 assert_eq!(
315 hashes_buffer.len(),
316 array.len(),
317 "hashes_buffer and array should be of equal length"
318 );
319
320 if array.null_count() == 0 {
321 if rehash {
322 for (hash, &value) in hashes_buffer.iter_mut().zip(array.values().iter()) {
323 let mut hasher = random_state.seeded_state(*hash).build_hasher();
324 value.hash_write(&mut hasher);
325 *hash = hasher.finish();
326 }
327 } else {
328 for (hash, &value) in hashes_buffer.iter_mut().zip(array.values().iter()) {
329 *hash = value.hash_one(random_state);
330 }
331 }
332 } else if rehash {
333 for i in array.nulls().unwrap().valid_indices() {
334 let value = unsafe { array.value_unchecked(i) };
335 let mut hasher = random_state.seeded_state(hashes_buffer[i]).build_hasher();
336 value.hash_write(&mut hasher);
337 hashes_buffer[i] = hasher.finish();
338 }
339 } else {
340 for i in array.nulls().unwrap().valid_indices() {
341 let value = unsafe { array.value_unchecked(i) };
342 hashes_buffer[i] = value.hash_one(random_state);
343 }
344 }
345}
346
347#[cfg(not(feature = "force_hash_collisions"))]
351fn hash_array<T>(
352 array: &T,
353 random_state: &impl HashState,
354 hashes_buffer: &mut [u64],
355 rehash: bool,
356) where
357 T: ArrayAccessor,
358 T::Item: HashValue,
359{
360 assert_eq!(
361 hashes_buffer.len(),
362 array.len(),
363 "hashes_buffer and array should be of equal length"
364 );
365
366 if array.null_count() == 0 {
367 if rehash {
368 for (i, hash) in hashes_buffer.iter_mut().enumerate() {
369 let value = unsafe { array.value_unchecked(i) };
370 *hash = combine_hashes(value.hash_one(random_state), *hash);
371 }
372 } else {
373 for (i, hash) in hashes_buffer.iter_mut().enumerate() {
374 let value = unsafe { array.value_unchecked(i) };
375 *hash = value.hash_one(random_state);
376 }
377 }
378 } else if rehash {
379 for i in array.nulls().unwrap().valid_indices() {
380 let value = unsafe { array.value_unchecked(i) };
381 hashes_buffer[i] =
382 combine_hashes(value.hash_one(random_state), hashes_buffer[i]);
383 }
384 } else {
385 for i in array.nulls().unwrap().valid_indices() {
386 let value = unsafe { array.value_unchecked(i) };
387 hashes_buffer[i] = value.hash_one(random_state);
388 }
389 }
390}
391
392#[cfg(not(feature = "force_hash_collisions"))]
400#[inline(never)]
401fn hash_string_view_array_inner<
402 T: ByteViewType,
403 const HAS_NULLS: bool,
404 const HAS_BUFFERS: bool,
405 const REHASH: bool,
406>(
407 array: &GenericByteViewArray<T>,
408 random_state: &impl HashState,
409 hashes_buffer: &mut [u64],
410) {
411 assert_eq!(
412 hashes_buffer.len(),
413 array.len(),
414 "hashes_buffer and array should be of equal length"
415 );
416
417 let buffers = array.data_buffers();
418 let view_bytes = |view_len: u32, view: u128| {
419 let view = ByteView::from(view);
420 let offset = view.offset as usize;
421 unsafe {
423 let data = buffers.get_unchecked(view.buffer_index as usize);
424 data.get_unchecked(offset..offset + view_len as usize)
425 }
426 };
427
428 let hashes_and_views = hashes_buffer.iter_mut().zip(array.views().iter());
429 for (i, (hash, &v)) in hashes_and_views.enumerate() {
430 if HAS_NULLS && array.is_null(i) {
431 continue;
432 }
433 let view_len = v as u32;
434 if !HAS_BUFFERS || view_len <= 12 {
436 if REHASH {
437 let mut hasher = random_state.seeded_state(*hash).build_hasher();
438 v.hash_write(&mut hasher);
439 *hash = hasher.finish();
440 } else {
441 *hash = v.hash_one(random_state);
442 }
443 continue;
444 }
445 let value = view_bytes(view_len, v);
447 if REHASH {
448 let mut hasher = random_state.seeded_state(*hash).build_hasher();
449 value.hash_write(&mut hasher);
450 *hash = hasher.finish();
451 } else {
452 *hash = value.hash_one(random_state);
453 }
454 }
455}
456
457#[cfg(not(feature = "force_hash_collisions"))]
461fn hash_generic_byte_view_array<T: ByteViewType>(
462 array: &GenericByteViewArray<T>,
463 random_state: &impl HashState,
464 hashes_buffer: &mut [u64],
465 rehash: bool,
466) {
467 match (
469 array.null_count() != 0,
470 !array.data_buffers().is_empty(),
471 rehash,
472 ) {
473 (false, false, false) => {
476 for (hash, &view) in hashes_buffer.iter_mut().zip(array.views().iter()) {
477 *hash = view.hash_one(random_state);
478 }
479 }
480 (false, false, true) => {
481 for (hash, &view) in hashes_buffer.iter_mut().zip(array.views().iter()) {
482 let mut hasher = random_state.seeded_state(*hash).build_hasher();
483 view.hash_write(&mut hasher);
484 *hash = hasher.finish();
485 }
486 }
487 (false, true, false) => hash_string_view_array_inner::<T, false, true, false>(
488 array,
489 random_state,
490 hashes_buffer,
491 ),
492 (false, true, true) => hash_string_view_array_inner::<T, false, true, true>(
493 array,
494 random_state,
495 hashes_buffer,
496 ),
497 (true, false, false) => hash_string_view_array_inner::<T, true, false, false>(
498 array,
499 random_state,
500 hashes_buffer,
501 ),
502 (true, false, true) => hash_string_view_array_inner::<T, true, false, true>(
503 array,
504 random_state,
505 hashes_buffer,
506 ),
507 (true, true, false) => hash_string_view_array_inner::<T, true, true, false>(
508 array,
509 random_state,
510 hashes_buffer,
511 ),
512 (true, true, true) => hash_string_view_array_inner::<T, true, true, true>(
513 array,
514 random_state,
515 hashes_buffer,
516 ),
517 }
518}
519
520#[cfg(not(feature = "force_hash_collisions"))]
527#[inline(never)]
528fn hash_dictionary_scatter<
529 K: ArrowDictionaryKeyType,
530 const HAS_NULL_KEYS: bool,
531 const HAS_NULL_VALUES: bool,
532 const MULTI_COL: bool,
533>(
534 array: &DictionaryArray<K>,
535 dict_hashes: &[u64],
536 hashes_buffer: &mut [u64],
537) {
538 let dict_values = array.values();
539 if HAS_NULL_KEYS {
540 for (hash, key) in hashes_buffer.iter_mut().zip(array.keys().iter()) {
541 if let Some(key) = key {
542 let idx = key.as_usize();
543 if !HAS_NULL_VALUES || dict_values.is_valid(idx) {
544 if MULTI_COL {
545 *hash = combine_hashes(dict_hashes[idx], *hash);
546 } else {
547 *hash = dict_hashes[idx];
548 }
549 }
550 }
551 }
552 } else {
553 for (hash, key) in hashes_buffer.iter_mut().zip(array.keys().values()) {
554 let idx = key.as_usize();
555 if !HAS_NULL_VALUES || dict_values.is_valid(idx) {
556 if MULTI_COL {
557 *hash = combine_hashes(dict_hashes[idx], *hash);
558 } else {
559 *hash = dict_hashes[idx];
560 }
561 }
562 }
563 }
564}
565
566#[cfg(not(feature = "force_hash_collisions"))]
567fn dispatch_dictionary_scatter<K: ArrowDictionaryKeyType>(
568 array: &DictionaryArray<K>,
569 dict_hashes: &[u64],
570 hashes_buffer: &mut [u64],
571 multi_col: bool,
572) {
573 let has_null_keys = array.keys().null_count() != 0;
574 let has_null_values = array.values().null_count() != 0;
575
576 match (has_null_keys, has_null_values, multi_col) {
577 (false, false, false) => hash_dictionary_scatter::<K, false, false, false>(
578 array,
579 dict_hashes,
580 hashes_buffer,
581 ),
582 (false, false, true) => hash_dictionary_scatter::<K, false, false, true>(
583 array,
584 dict_hashes,
585 hashes_buffer,
586 ),
587 (false, true, false) => hash_dictionary_scatter::<K, false, true, false>(
588 array,
589 dict_hashes,
590 hashes_buffer,
591 ),
592 (false, true, true) => hash_dictionary_scatter::<K, false, true, true>(
593 array,
594 dict_hashes,
595 hashes_buffer,
596 ),
597 (true, false, false) => hash_dictionary_scatter::<K, true, false, false>(
598 array,
599 dict_hashes,
600 hashes_buffer,
601 ),
602 (true, false, true) => hash_dictionary_scatter::<K, true, false, true>(
603 array,
604 dict_hashes,
605 hashes_buffer,
606 ),
607 (true, true, false) => hash_dictionary_scatter::<K, true, true, false>(
608 array,
609 dict_hashes,
610 hashes_buffer,
611 ),
612 (true, true, true) => hash_dictionary_scatter::<K, true, true, true>(
613 array,
614 dict_hashes,
615 hashes_buffer,
616 ),
617 }
618}
619
620#[cfg(not(feature = "force_hash_collisions"))]
622fn hash_dictionary<K: ArrowDictionaryKeyType>(
623 array: &DictionaryArray<K>,
624 random_state: &impl HashState,
625 hashes_buffer: &mut [u64],
626 multi_col: bool,
627) -> Result<()> {
628 let dict_values = array.values();
632 let mut dict_hashes = vec![0; dict_values.len()];
633 create_hashes([dict_values], random_state, &mut dict_hashes)?;
634 dispatch_dictionary_scatter(array, &dict_hashes, hashes_buffer, multi_col);
635 Ok(())
636}
637
638#[cfg(not(feature = "force_hash_collisions"))]
639fn hash_dictionary_with_child_hashing<K: ArrowDictionaryKeyType>(
640 array: &DictionaryArray<K>,
641 child_hashing: &impl ChildHashing,
642 hashes_buffer: &mut [u64],
643 multi_col: bool,
644) -> Result<()> {
645 let dict_values = array.values();
646 let mut dict_hashes = vec![0; dict_values.len()];
647 child_hashing.create_hashes([dict_values], &mut dict_hashes)?;
648 dispatch_dictionary_scatter(array, &dict_hashes, hashes_buffer, multi_col);
649 Ok(())
650}
651
652#[cfg(not(feature = "force_hash_collisions"))]
653fn hash_struct_array(
654 array: &StructArray,
655 child_hashing: &impl ChildHashing,
656 hashes_buffer: &mut [u64],
657) -> Result<()> {
658 let nulls = array.nulls();
659 let row_len = array.len();
660
661 let mut values_hashes = vec![0u64; row_len];
663 child_hashing.create_hashes(array.columns(), &mut values_hashes)?;
664
665 if let Some(nulls) = nulls {
667 for i in nulls.valid_indices() {
668 let hash = &mut hashes_buffer[i];
669 *hash = combine_hashes(*hash, values_hashes[i]);
670 }
671 } else {
672 for i in 0..row_len {
673 let hash = &mut hashes_buffer[i];
674 *hash = combine_hashes(*hash, values_hashes[i]);
675 }
676 }
677
678 Ok(())
679}
680
681#[cfg(not(feature = "force_hash_collisions"))]
683fn hash_map_array(
684 array: &MapArray,
685 child_hashing: &impl ChildHashing,
686 hashes_buffer: &mut [u64],
687) -> Result<()> {
688 let nulls = array.nulls();
689 let offsets = array.offsets();
690
691 let first_offset = offsets.first().copied().unwrap_or_default() as usize;
693 let last_offset = offsets.last().copied().unwrap_or_default() as usize;
694 let entries_len = last_offset - first_offset;
695
696 let mut values_hashes = vec![0u64; entries_len];
698 let entries = array.entries();
699 let sliced_columns: Vec<ArrayRef> = entries
700 .columns()
701 .iter()
702 .map(|col| col.slice(first_offset, entries_len))
703 .collect();
704 child_hashing.create_hashes(&sliced_columns, &mut values_hashes)?;
705
706 if let Some(nulls) = nulls {
709 for (i, (start, stop)) in offsets.iter().zip(offsets.iter().skip(1)).enumerate() {
710 if nulls.is_valid(i) {
711 let hash = &mut hashes_buffer[i];
712 for values_hash in &values_hashes
713 [start.as_usize() - first_offset..stop.as_usize() - first_offset]
714 {
715 *hash = combine_hashes(*hash, *values_hash);
716 }
717 }
718 }
719 } else {
720 for (i, (start, stop)) in offsets.iter().zip(offsets.iter().skip(1)).enumerate() {
721 let hash = &mut hashes_buffer[i];
722 for values_hash in &values_hashes
723 [start.as_usize() - first_offset..stop.as_usize() - first_offset]
724 {
725 *hash = combine_hashes(*hash, *values_hash);
726 }
727 }
728 }
729
730 Ok(())
731}
732
733#[cfg(not(feature = "force_hash_collisions"))]
734fn hash_list_array<OffsetSize>(
735 array: &GenericListArray<OffsetSize>,
736 child_hashing: &impl ChildHashing,
737 hashes_buffer: &mut [u64],
738) -> Result<()>
739where
740 OffsetSize: OffsetSizeTrait,
741{
742 let first_offset = array.value_offsets().first().cloned().unwrap_or_default();
744 let last_offset = array.value_offsets().last().cloned().unwrap_or_default();
745 let value_bytes_len = (last_offset - first_offset).as_usize();
746 let mut values_hashes = vec![0u64; value_bytes_len];
747 child_hashing.create_hashes(
748 [array
749 .values()
750 .slice(first_offset.as_usize(), value_bytes_len)],
751 &mut values_hashes,
752 )?;
753
754 if array.null_count() > 0 {
755 for (i, (start, stop)) in array.value_offsets().iter().tuple_windows().enumerate()
756 {
757 if array.is_valid(i) {
758 let hash = &mut hashes_buffer[i];
759 for values_hash in &values_hashes[(*start - first_offset).as_usize()
760 ..(*stop - first_offset).as_usize()]
761 {
762 *hash = combine_hashes(*hash, *values_hash);
763 }
764 }
765 }
766 } else {
767 for ((start, stop), hash) in array
768 .value_offsets()
769 .iter()
770 .tuple_windows()
771 .zip(hashes_buffer.iter_mut())
772 {
773 for values_hash in &values_hashes
774 [(*start - first_offset).as_usize()..(*stop - first_offset).as_usize()]
775 {
776 *hash = combine_hashes(*hash, *values_hash);
777 }
778 }
779 }
780 Ok(())
781}
782
783#[cfg(not(feature = "force_hash_collisions"))]
784fn hash_list_view_array<OffsetSize>(
785 array: &GenericListViewArray<OffsetSize>,
786 child_hashing: &impl ChildHashing,
787 hashes_buffer: &mut [u64],
788) -> Result<()>
789where
790 OffsetSize: OffsetSizeTrait,
791{
792 let values = array.values();
793 let offsets = array.value_offsets();
794 let sizes = array.value_sizes();
795 let nulls = array.nulls();
796 let mut values_hashes = vec![0u64; values.len()];
797 child_hashing.create_hashes([values], &mut values_hashes)?;
798 if let Some(nulls) = nulls {
799 for (i, (offset, size)) in offsets.iter().zip(sizes.iter()).enumerate() {
800 if nulls.is_valid(i) {
801 let hash = &mut hashes_buffer[i];
802 let start = offset.as_usize();
803 let end = start + size.as_usize();
804 for values_hash in &values_hashes[start..end] {
805 *hash = combine_hashes(*hash, *values_hash);
806 }
807 }
808 }
809 } else {
810 for (i, (offset, size)) in offsets.iter().zip(sizes.iter()).enumerate() {
811 let hash = &mut hashes_buffer[i];
812 let start = offset.as_usize();
813 let end = start + size.as_usize();
814 for values_hash in &values_hashes[start..end] {
815 *hash = combine_hashes(*hash, *values_hash);
816 }
817 }
818 }
819 Ok(())
820}
821
822#[cfg(not(feature = "force_hash_collisions"))]
823fn hash_union_array(
824 array: &UnionArray,
825 child_hashing: &impl ChildHashing,
826 hashes_buffer: &mut [u64],
827) -> Result<()> {
828 let DataType::Union(union_fields, _mode) = array.data_type() else {
829 unreachable!()
830 };
831
832 if array.is_dense() {
833 hash_union_array_default(array, union_fields, child_hashing, hashes_buffer)
836 } else {
837 hash_sparse_union_array(array, union_fields, child_hashing, hashes_buffer)
841 }
842}
843
844#[cfg(not(feature = "force_hash_collisions"))]
854fn hash_union_array_default(
855 array: &UnionArray,
856 union_fields: &UnionFields,
857 child_hashing: &impl ChildHashing,
858 hashes_buffer: &mut [u64],
859) -> Result<()> {
860 let mut child_hashes: HashMap<i8, Vec<u64>> =
861 HashMap::with_capacity(union_fields.len());
862
863 for (type_id, _field) in union_fields.iter() {
865 let child = array.child(type_id);
866 let mut child_hash_buffer = vec![0; child.len()];
867 child_hashing.create_hashes([child], &mut child_hash_buffer)?;
868
869 child_hashes.insert(type_id, child_hash_buffer);
870 }
871
872 #[expect(clippy::needless_range_loop)]
876 for i in 0..array.len() {
877 let type_id = array.type_id(i);
878 let child_offset = array.value_offset(i);
879
880 let child_hash = child_hashes.get(&type_id).expect("invalid type_id");
881 hashes_buffer[i] = combine_hashes(hashes_buffer[i], child_hash[child_offset]);
882 }
883
884 Ok(())
885}
886
887#[cfg(not(feature = "force_hash_collisions"))]
895fn hash_sparse_union_array(
896 array: &UnionArray,
897 union_fields: &UnionFields,
898 child_hashing: &impl ChildHashing,
899 hashes_buffer: &mut [u64],
900) -> Result<()> {
901 use std::collections::HashMap;
902
903 if union_fields.len() <= 2 {
906 return hash_union_array_default(
907 array,
908 union_fields,
909 child_hashing,
910 hashes_buffer,
911 );
912 }
913
914 let type_ids = array.type_ids();
915
916 let mut indices_by_type: HashMap<i8, Vec<u32>> = HashMap::new();
918 for (i, &type_id) in type_ids.iter().enumerate() {
919 indices_by_type.entry(type_id).or_default().push(i as u32);
920 }
921
922 for (type_id, _field) in union_fields.iter() {
924 if let Some(indices) = indices_by_type.get(&type_id) {
925 if indices.is_empty() {
926 continue;
927 }
928
929 let child = array.child(type_id);
930 let indices_array = UInt32Array::from(indices.clone());
931
932 let filtered = take(child.as_ref(), &indices_array, None)?;
934
935 let mut filtered_hashes = vec![0u64; filtered.len()];
937 child_hashing.create_hashes([&filtered], &mut filtered_hashes)?;
938
939 for (hash, &idx) in filtered_hashes.iter().zip(indices.iter()) {
941 hashes_buffer[idx as usize] =
942 combine_hashes(hashes_buffer[idx as usize], *hash);
943 }
944 }
945 }
946
947 Ok(())
948}
949
950#[cfg(not(feature = "force_hash_collisions"))]
951fn hash_fixed_list_array(
952 array: &FixedSizeListArray,
953 child_hashing: &impl ChildHashing,
954 hashes_buffer: &mut [u64],
955) -> Result<()> {
956 let values = array.values();
957 let value_length = array.value_length() as usize;
958 let nulls = array.nulls();
959 let mut values_hashes = vec![0u64; values.len()];
960 child_hashing.create_hashes([values], &mut values_hashes)?;
961 if let Some(nulls) = nulls {
962 for i in 0..array.len() {
963 if nulls.is_valid(i) {
964 let hash = &mut hashes_buffer[i];
965 for values_hash in
966 &values_hashes[i * value_length..(i + 1) * value_length]
967 {
968 *hash = combine_hashes(*hash, *values_hash);
969 }
970 }
971 }
972 } else {
973 for i in 0..array.len() {
974 let hash = &mut hashes_buffer[i];
975 for values_hash in &values_hashes[i * value_length..(i + 1) * value_length] {
976 *hash = combine_hashes(*hash, *values_hash);
977 }
978 }
979 }
980 Ok(())
981}
982
983#[inline(never)]
985#[cfg(not(feature = "force_hash_collisions"))]
986fn hash_run_array_inner<
987 R: RunEndIndexType,
988 C: ChildHashing + ?Sized,
989 const HAS_NULL_VALUES: bool,
990 const REHASH: bool,
991>(
992 array: &RunArray<R>,
993 child_hashing: &C,
994 hashes_buffer: &mut [u64],
995) -> Result<()> {
996 let array_offset = array.offset();
1000 let array_len = array.len();
1001
1002 if array_len == 0 {
1003 return Ok(());
1004 }
1005
1006 let run_ends = array.run_ends();
1007 let run_ends_values = run_ends.values();
1008 let values = array.values();
1009
1010 let start_physical_index = array.get_start_physical_index();
1011 let end_physical_index = array.get_end_physical_index() + 1;
1014
1015 let sliced_values = values.slice(
1016 start_physical_index,
1017 end_physical_index - start_physical_index,
1018 );
1019 let mut values_hashes = vec![0u64; sliced_values.len()];
1020 child_hashing
1021 .create_hashes(std::slice::from_ref(&sliced_values), &mut values_hashes)?;
1022
1023 let mut start_in_slice = 0;
1024 for (adjusted_physical_index, &absolute_run_end) in run_ends_values
1025 [start_physical_index..end_physical_index]
1026 .iter()
1027 .enumerate()
1028 {
1029 let absolute_run_end = absolute_run_end.as_usize();
1030 let end_in_slice = (absolute_run_end - array_offset).min(array_len);
1031
1032 if HAS_NULL_VALUES && sliced_values.is_null(adjusted_physical_index) {
1033 start_in_slice = end_in_slice;
1034 continue;
1035 }
1036
1037 let value_hash = values_hashes[adjusted_physical_index];
1038 let run_slice = &mut hashes_buffer[start_in_slice..end_in_slice];
1039
1040 if REHASH {
1041 for hash in run_slice.iter_mut() {
1042 *hash = combine_hashes(value_hash, *hash);
1043 }
1044 } else {
1045 run_slice.fill(value_hash);
1046 }
1047
1048 start_in_slice = end_in_slice;
1049 }
1050
1051 Ok(())
1052}
1053
1054#[cfg(not(feature = "force_hash_collisions"))]
1055fn hash_run_array<R: RunEndIndexType>(
1056 array: &RunArray<R>,
1057 child_hashing: &impl ChildHashing,
1058 hashes_buffer: &mut [u64],
1059 rehash: bool,
1060) -> Result<()> {
1061 let has_null_values = array.values().null_count() != 0;
1062
1063 match (has_null_values, rehash) {
1064 (false, false) => hash_run_array_inner::<R, _, false, false>(
1065 array,
1066 child_hashing,
1067 hashes_buffer,
1068 ),
1069 (false, true) => {
1070 hash_run_array_inner::<R, _, false, true>(array, child_hashing, hashes_buffer)
1071 }
1072 (true, false) => {
1073 hash_run_array_inner::<R, _, true, false>(array, child_hashing, hashes_buffer)
1074 }
1075 (true, true) => {
1076 hash_run_array_inner::<R, _, true, true>(array, child_hashing, hashes_buffer)
1077 }
1078 }
1079}
1080
1081#[cfg(not(feature = "force_hash_collisions"))]
1084fn hash_single_array(
1085 array: &dyn Array,
1086 random_state: &impl HashState,
1087 hashes_buffer: &mut [u64],
1088 rehash: bool,
1089) -> Result<()> {
1090 downcast_primitive_array! {
1091 array => hash_array_primitive(array, random_state, hashes_buffer, rehash),
1092 DataType::Null => hash_null(random_state, hashes_buffer, rehash),
1093 DataType::Boolean => hash_array(&as_boolean_array(array)?, random_state, hashes_buffer, rehash),
1094 DataType::Utf8 => hash_array(&as_string_array(array)?, random_state, hashes_buffer, rehash),
1095 DataType::Utf8View => hash_generic_byte_view_array(as_string_view_array(array)?, random_state, hashes_buffer, rehash),
1096 DataType::LargeUtf8 => hash_array(&as_largestring_array(array), random_state, hashes_buffer, rehash),
1097 DataType::Binary => hash_array(&as_generic_binary_array::<i32>(array)?, random_state, hashes_buffer, rehash),
1098 DataType::BinaryView => hash_generic_byte_view_array(as_binary_view_array(array)?, random_state, hashes_buffer, rehash),
1099 DataType::LargeBinary => hash_array(&as_generic_binary_array::<i64>(array)?, random_state, hashes_buffer, rehash),
1100 DataType::FixedSizeBinary(_) => {
1101 let array: &FixedSizeBinaryArray = array.as_any().downcast_ref().unwrap();
1102 hash_array(&array, random_state, hashes_buffer, rehash)
1103 }
1104 DataType::Dictionary(_, _) => downcast_dictionary_array! {
1105 array => hash_dictionary(array, random_state, hashes_buffer, rehash)?,
1106 _ => unreachable!()
1107 }
1108 DataType::Struct(_) => {
1109 let array = as_struct_array(array)?;
1110 let child_hashing = HashStateChildHashing {
1111 hash_state: random_state,
1112 };
1113 hash_struct_array(array, &child_hashing, hashes_buffer)?;
1114 }
1115 DataType::List(_) => {
1116 let array = as_list_array(array)?;
1117 let child_hashing = HashStateChildHashing {
1118 hash_state: random_state,
1119 };
1120 hash_list_array(array, &child_hashing, hashes_buffer)?;
1121 }
1122 DataType::LargeList(_) => {
1123 let array = as_large_list_array(array)?;
1124 let child_hashing = HashStateChildHashing {
1125 hash_state: random_state,
1126 };
1127 hash_list_array(array, &child_hashing, hashes_buffer)?;
1128 }
1129 DataType::ListView(_) => {
1130 let array = as_list_view_array(array)?;
1131 let child_hashing = HashStateChildHashing {
1132 hash_state: random_state,
1133 };
1134 hash_list_view_array(array, &child_hashing, hashes_buffer)?;
1135 }
1136 DataType::LargeListView(_) => {
1137 let array = as_large_list_view_array(array)?;
1138 let child_hashing = HashStateChildHashing {
1139 hash_state: random_state,
1140 };
1141 hash_list_view_array(array, &child_hashing, hashes_buffer)?;
1142 }
1143 DataType::Map(_, _) => {
1144 let array = as_map_array(array)?;
1145 let child_hashing = HashStateChildHashing {
1146 hash_state: random_state,
1147 };
1148 hash_map_array(array, &child_hashing, hashes_buffer)?;
1149 }
1150 DataType::FixedSizeList(_,_) => {
1151 let array = as_fixed_size_list_array(array)?;
1152 let child_hashing = HashStateChildHashing {
1153 hash_state: random_state,
1154 };
1155 hash_fixed_list_array(array, &child_hashing, hashes_buffer)?;
1156 }
1157 DataType::Union(_, _) => {
1158 let array = as_union_array(array)?;
1159 let child_hashing = HashStateChildHashing {
1160 hash_state: random_state,
1161 };
1162 hash_union_array(array, &child_hashing, hashes_buffer)?;
1163 }
1164 DataType::RunEndEncoded(_, _) => downcast_run_array! {
1165 array => {
1166 let child_hashing = HashStateChildHashing {
1167 hash_state: random_state,
1168 };
1169 hash_run_array(array, &child_hashing, hashes_buffer, rehash)?
1170 },
1171 _ => unreachable!()
1172 }
1173 _ => {
1174 return _internal_err!(
1176 "Unsupported data type in hasher: {}",
1177 array.data_type()
1178 );
1179 }
1180 }
1181 Ok(())
1182}
1183
1184#[cfg(feature = "force_hash_collisions")]
1186fn hash_single_array(
1187 _array: &dyn Array,
1188 _random_state: &impl HashState,
1189 hashes_buffer: &mut [u64],
1190 _rehash: bool,
1191) -> Result<()> {
1192 for hash in hashes_buffer.iter_mut() {
1193 *hash = 0
1194 }
1195 Ok(())
1196}
1197
1198pub trait AsDynArray {
1208 fn as_dyn_array(&self) -> &dyn Array;
1209}
1210
1211impl AsDynArray for dyn Array {
1212 fn as_dyn_array(&self) -> &dyn Array {
1213 self
1214 }
1215}
1216
1217impl AsDynArray for &dyn Array {
1218 fn as_dyn_array(&self) -> &dyn Array {
1219 *self
1220 }
1221}
1222
1223impl AsDynArray for ArrayRef {
1224 fn as_dyn_array(&self) -> &dyn Array {
1225 self.as_ref()
1226 }
1227}
1228
1229impl AsDynArray for &ArrayRef {
1230 fn as_dyn_array(&self) -> &dyn Array {
1231 self.as_ref()
1232 }
1233}
1234
1235pub fn create_hashes<'a, I, T>(
1240 arrays: I,
1241 random_state: &impl HashState,
1242 hashes_buffer: &'a mut [u64],
1243) -> Result<&'a mut [u64]>
1244where
1245 I: IntoIterator<Item = T>,
1246 T: AsDynArray,
1247{
1248 for (i, array) in arrays.into_iter().enumerate() {
1249 let rehash = i >= 1;
1251 hash_single_array(array.as_dyn_array(), random_state, hashes_buffer, rehash)?;
1252 }
1253 Ok(hashes_buffer)
1254}
1255
1256pub fn create_hashes_with_hasher<'a, I, T, S>(
1270 arrays: I,
1271 hash_builder: &S,
1272 hashes_buffer: &'a mut [u64],
1273) -> Result<&'a mut [u64]>
1274where
1275 I: IntoIterator<Item = T>,
1276 T: AsDynArray,
1277 S: BuildHasher,
1278{
1279 build_hasher::create_hashes_with_hasher(arrays, hash_builder, hashes_buffer)
1280}
1281
1282#[cfg(test)]
1283mod tests {
1284 #[cfg(not(feature = "force_hash_collisions"))]
1285 use std::hash::{BuildHasherDefault, Hasher};
1286 use std::sync::Arc;
1287
1288 use arrow::array::*;
1289 #[cfg(not(feature = "force_hash_collisions"))]
1290 use arrow::datatypes::*;
1291
1292 use super::*;
1293
1294 #[cfg(not(feature = "force_hash_collisions"))]
1295 #[derive(Default)]
1296 struct TestHasher(u64);
1297
1298 #[cfg(not(feature = "force_hash_collisions"))]
1299 impl Hasher for TestHasher {
1300 fn finish(&self) -> u64 {
1301 self.0
1302 }
1303
1304 fn write(&mut self, bytes: &[u8]) {
1305 for byte in bytes {
1306 self.0 = self.0.wrapping_mul(37).wrapping_add(u64::from(*byte));
1307 }
1308 }
1309 }
1310
1311 #[test]
1312 fn create_hashes_for_decimal_array() -> Result<()> {
1313 let array = vec![1, 2, 3, 4]
1314 .into_iter()
1315 .map(Some)
1316 .collect::<Decimal128Array>()
1317 .with_precision_and_scale(20, 3)
1318 .unwrap();
1319 let array_ref: ArrayRef = Arc::new(array);
1320 let random_state = RandomState::with_seed(0);
1321 let hashes_buff = &mut vec![0; array_ref.len()];
1322 let hashes = create_hashes(&[array_ref], &random_state, hashes_buff)?;
1323 assert_eq!(hashes.len(), 4);
1324 Ok(())
1325 }
1326
1327 #[test]
1328 fn create_hashes_for_empty_fixed_size_lit() -> Result<()> {
1329 let empty_array = FixedSizeListBuilder::new(StringBuilder::new(), 1).finish();
1330 let random_state = RandomState::with_seed(0);
1331 let hashes_buff = &mut [0; 0];
1332 let hashes = create_hashes(
1333 &[Arc::new(empty_array) as ArrayRef],
1334 &random_state,
1335 hashes_buff,
1336 )?;
1337 assert_eq!(hashes, &Vec::<u64>::new());
1338 Ok(())
1339 }
1340
1341 #[test]
1342 fn create_hashes_for_float_arrays() -> Result<()> {
1343 let f32_arr: ArrayRef =
1344 Arc::new(Float32Array::from(vec![0.12, 0.5, 1f32, 444.7]));
1345 let f64_arr: ArrayRef =
1346 Arc::new(Float64Array::from(vec![0.12, 0.5, 1f64, 444.7]));
1347
1348 let random_state = RandomState::with_seed(0);
1349 let hashes_buff = &mut vec![0; f32_arr.len()];
1350 let hashes = create_hashes(&[f32_arr], &random_state, hashes_buff)?;
1351 assert_eq!(hashes.len(), 4,);
1352
1353 let hashes = create_hashes(&[f64_arr], &random_state, hashes_buff)?;
1354 assert_eq!(hashes.len(), 4,);
1355
1356 Ok(())
1357 }
1358
1359 macro_rules! create_hash_binary {
1360 ($NAME:ident, $ARRAY:ty) => {
1361 #[cfg(not(feature = "force_hash_collisions"))]
1362 #[test]
1363 fn $NAME() {
1364 let binary = [
1365 Some(b"short".to_byte_slice()),
1366 None,
1367 Some(b"long but different 12 bytes string"),
1368 Some(b"short2"),
1369 Some(b"Longer than 12 bytes string"),
1370 Some(b"short"),
1371 Some(b"Longer than 12 bytes string"),
1372 ];
1373
1374 let binary_array: ArrayRef =
1375 Arc::new(binary.iter().cloned().collect::<$ARRAY>());
1376
1377 let random_state = RandomState::with_seed(0);
1378
1379 let mut binary_hashes = vec![0; binary.len()];
1380 create_hashes(&[binary_array], &random_state, &mut binary_hashes)
1381 .unwrap();
1382
1383 for (val, hash) in binary.iter().zip(binary_hashes.iter()) {
1385 match val {
1386 Some(_) => assert_ne!(*hash, 0),
1387 None => assert_eq!(*hash, 0),
1388 }
1389 }
1390
1391 assert_eq!(binary[0], binary[5]);
1393 assert_eq!(binary[4], binary[6]);
1394
1395 assert_ne!(binary[0], binary[2]);
1397 }
1398 };
1399 }
1400
1401 create_hash_binary!(binary_array, BinaryArray);
1402 create_hash_binary!(large_binary_array, LargeBinaryArray);
1403 create_hash_binary!(binary_view_array, BinaryViewArray);
1404
1405 #[test]
1406 fn create_hashes_fixed_size_binary() -> Result<()> {
1407 let input_arg = vec![vec![1, 2], vec![5, 6], vec![5, 6]];
1408 let fixed_size_binary_array: ArrayRef =
1409 Arc::new(FixedSizeBinaryArray::try_from_iter(input_arg.into_iter()).unwrap());
1410
1411 let random_state = RandomState::with_seed(0);
1412 let hashes_buff = &mut vec![0; fixed_size_binary_array.len()];
1413 let hashes =
1414 create_hashes(&[fixed_size_binary_array], &random_state, hashes_buff)?;
1415 assert_eq!(hashes.len(), 3,);
1416
1417 Ok(())
1418 }
1419
1420 macro_rules! create_hash_string {
1421 ($NAME:ident, $ARRAY:ty) => {
1422 #[cfg(not(feature = "force_hash_collisions"))]
1423 #[test]
1424 fn $NAME() {
1425 let strings = [
1426 Some("short"),
1427 None,
1428 Some("long but different 12 bytes string"),
1429 Some("short2"),
1430 Some("Longer than 12 bytes string"),
1431 Some("short"),
1432 Some("Longer than 12 bytes string"),
1433 ];
1434
1435 let string_array: ArrayRef =
1436 Arc::new(strings.iter().cloned().collect::<$ARRAY>());
1437 let dict_array: ArrayRef = Arc::new(
1438 strings
1439 .iter()
1440 .cloned()
1441 .collect::<DictionaryArray<Int8Type>>(),
1442 );
1443
1444 let random_state = RandomState::with_seed(0);
1445
1446 let mut string_hashes = vec![0; strings.len()];
1447 create_hashes(&[string_array], &random_state, &mut string_hashes)
1448 .unwrap();
1449
1450 let mut dict_hashes = vec![0; strings.len()];
1451 create_hashes(&[dict_array], &random_state, &mut dict_hashes).unwrap();
1452
1453 for (val, hash) in strings.iter().zip(string_hashes.iter()) {
1455 match val {
1456 Some(_) => assert_ne!(*hash, 0),
1457 None => assert_eq!(*hash, 0),
1458 }
1459 }
1460
1461 assert_eq!(string_hashes, dict_hashes);
1463
1464 assert_eq!(strings[0], strings[5]);
1466 assert_eq!(strings[4], strings[6]);
1467
1468 assert_ne!(strings[0], strings[2]);
1470 }
1471 };
1472 }
1473
1474 create_hash_string!(string_array, StringArray);
1475 create_hash_string!(large_string_array, LargeStringArray);
1476 create_hash_string!(string_view_array, StringArray);
1477 create_hash_string!(dict_string_array, DictionaryArray<Int8Type>);
1478
1479 #[test]
1480 #[cfg(not(feature = "force_hash_collisions"))]
1481 fn create_hashes_for_run_array() -> Result<()> {
1482 let values = Arc::new(Int32Array::from(vec![10, 20, 30]));
1483 let run_ends = Arc::new(Int32Array::from(vec![2, 5, 7]));
1484 let array = Arc::new(RunArray::try_new(&run_ends, values.as_ref()).unwrap());
1485
1486 let random_state = RandomState::with_seed(0);
1487 let hashes_buff = &mut vec![0; array.len()];
1488 let hashes = create_hashes(
1489 &[Arc::clone(&array) as ArrayRef],
1490 &random_state,
1491 hashes_buff,
1492 )?;
1493
1494 assert_eq!(hashes.len(), 7);
1495 assert_eq!(hashes[0], hashes[1]);
1496 assert_eq!(hashes[2], hashes[3]);
1497 assert_eq!(hashes[3], hashes[4]);
1498 assert_eq!(hashes[5], hashes[6]);
1499 assert_ne!(hashes[0], hashes[2]);
1500 assert_ne!(hashes[2], hashes[5]);
1501 assert_ne!(hashes[0], hashes[5]);
1502
1503 Ok(())
1504 }
1505
1506 #[test]
1507 #[cfg(not(feature = "force_hash_collisions"))]
1508 fn create_multi_column_hash_with_run_array() -> Result<()> {
1509 let int_array = Arc::new(Int32Array::from(vec![1, 2, 3, 4, 5, 6, 7]));
1510 let values = Arc::new(StringArray::from(vec!["foo", "bar", "baz"]));
1511 let run_ends = Arc::new(Int32Array::from(vec![2, 5, 7]));
1512 let run_array = Arc::new(RunArray::try_new(&run_ends, values.as_ref()).unwrap());
1513
1514 let random_state = RandomState::with_seed(0);
1515 let mut one_col_hashes = vec![0; int_array.len()];
1516 create_hashes(
1517 &[Arc::clone(&int_array) as ArrayRef],
1518 &random_state,
1519 &mut one_col_hashes,
1520 )?;
1521
1522 let mut two_col_hashes = vec![0; int_array.len()];
1523 create_hashes(
1524 &[
1525 Arc::clone(&int_array) as ArrayRef,
1526 Arc::clone(&run_array) as ArrayRef,
1527 ],
1528 &random_state,
1529 &mut two_col_hashes,
1530 )?;
1531
1532 assert_eq!(one_col_hashes.len(), 7);
1533 assert_eq!(two_col_hashes.len(), 7);
1534 assert_ne!(one_col_hashes, two_col_hashes);
1535
1536 let diff_0_vs_1_one_col = one_col_hashes[0] != one_col_hashes[1];
1537 let diff_0_vs_1_two_col = two_col_hashes[0] != two_col_hashes[1];
1538 assert_eq!(diff_0_vs_1_one_col, diff_0_vs_1_two_col);
1539
1540 let diff_2_vs_3_one_col = one_col_hashes[2] != one_col_hashes[3];
1541 let diff_2_vs_3_two_col = two_col_hashes[2] != two_col_hashes[3];
1542 assert_eq!(diff_2_vs_3_one_col, diff_2_vs_3_two_col);
1543
1544 Ok(())
1545 }
1546
1547 #[test]
1548 #[cfg(not(feature = "force_hash_collisions"))]
1549 fn test_create_hashes_with_custom_hasher() {
1550 let array: ArrayRef = Arc::new(Int32Array::from(vec![1, 2, 1, 4]));
1551 let hash_builder = BuildHasherDefault::<TestHasher>::default();
1552
1553 let mut custom_hashes = vec![0; array.len()];
1554 create_hashes_with_hasher([&array], &hash_builder, &mut custom_hashes).unwrap();
1555
1556 let random_state = RandomState::with_seed(0);
1557 let mut default_hashes = vec![0; array.len()];
1558 create_hashes([&array], &random_state, &mut default_hashes).unwrap();
1559
1560 assert_eq!(custom_hashes[0], custom_hashes[2]);
1561 assert_ne!(custom_hashes[0], custom_hashes[1]);
1562 assert_ne!(custom_hashes, default_hashes);
1563 }
1564
1565 #[test]
1566 #[cfg(not(feature = "force_hash_collisions"))]
1567 fn test_create_hashes_with_custom_hasher_normalizes_negative_zero() {
1568 let array: ArrayRef = Arc::new(Float64Array::from(vec![0.0, -0.0]));
1569 let hash_builder = BuildHasherDefault::<TestHasher>::default();
1570 let mut hashes = vec![0; array.len()];
1571
1572 create_hashes_with_hasher([&array], &hash_builder, &mut hashes).unwrap();
1573
1574 assert_eq!(hashes[0], hashes[1]);
1575 }
1576
1577 #[test]
1578 #[cfg(not(feature = "force_hash_collisions"))]
1579 fn test_create_hashes_dictionary_with_custom_hasher() {
1580 let strings = [Some("foo"), None, Some("bar"), Some("foo"), None];
1581 let string_array: ArrayRef =
1582 Arc::new(strings.iter().cloned().collect::<StringArray>());
1583 let dict_array: ArrayRef = Arc::new(
1584 strings
1585 .iter()
1586 .cloned()
1587 .collect::<DictionaryArray<Int8Type>>(),
1588 );
1589 let hash_builder = BuildHasherDefault::<TestHasher>::default();
1590
1591 let mut string_hashes = vec![0; strings.len()];
1592 create_hashes_with_hasher([&string_array], &hash_builder, &mut string_hashes)
1593 .unwrap();
1594
1595 let mut dict_hashes = vec![0; strings.len()];
1596 create_hashes_with_hasher([&dict_array], &hash_builder, &mut dict_hashes)
1597 .unwrap();
1598
1599 assert_eq!(string_hashes, dict_hashes);
1600 }
1601
1602 #[test]
1603 #[cfg(not(feature = "force_hash_collisions"))]
1604 fn test_create_hashes_struct_with_custom_hasher() {
1605 let struct_array = StructArray::from(vec![
1606 (
1607 Arc::new(Field::new("int", DataType::Int32, false)),
1608 Arc::new(Int32Array::from(vec![1, 2, 1, 3])) as ArrayRef,
1609 ),
1610 (
1611 Arc::new(Field::new("string", DataType::Utf8, false)),
1612 Arc::new(StringArray::from(vec!["alpha", "beta", "alpha", "alpha"]))
1613 as ArrayRef,
1614 ),
1615 ]);
1616 let hash_builder = BuildHasherDefault::<TestHasher>::default();
1617
1618 let mut child_hashes = vec![0; struct_array.len()];
1619 create_hashes_with_hasher(
1620 struct_array.columns(),
1621 &hash_builder,
1622 &mut child_hashes,
1623 )
1624 .unwrap();
1625 let expected_hashes = child_hashes
1626 .into_iter()
1627 .map(|hash| combine_hashes(0, hash))
1628 .collect::<Vec<_>>();
1629
1630 let array: ArrayRef = Arc::new(struct_array);
1631 let mut actual_hashes = vec![0; array.len()];
1632 create_hashes_with_hasher([&array], &hash_builder, &mut actual_hashes).unwrap();
1633
1634 assert_eq!(actual_hashes, expected_hashes);
1635 assert_eq!(actual_hashes[0], actual_hashes[2]);
1636 assert_ne!(actual_hashes[0], actual_hashes[3]);
1637 }
1638
1639 #[test]
1640 #[cfg(not(feature = "force_hash_collisions"))]
1641 fn test_create_hashes_long_utf8_view_with_custom_hasher() {
1642 let values = vec![
1643 Some("this string is longer than twelve bytes"),
1644 None,
1645 Some("another string longer than twelve bytes"),
1646 Some("this string is longer than twelve bytes"),
1647 ];
1648 let view_array = StringViewArray::from(values.clone());
1649 assert!(!view_array.data_buffers().is_empty());
1650 let view_array: ArrayRef = Arc::new(view_array);
1651 let hash_builder = BuildHasherDefault::<TestHasher>::default();
1652
1653 let mut view_hashes = vec![0; view_array.len()];
1654 create_hashes_with_hasher([&view_array], &hash_builder, &mut view_hashes)
1655 .unwrap();
1656 let expected_hashes = values
1657 .iter()
1658 .map(|value| {
1659 value
1660 .map(|value| hash_builder.hash_one(value.as_bytes()))
1661 .unwrap_or_default()
1662 })
1663 .collect::<Vec<_>>();
1664 assert_eq!(view_hashes, expected_hashes);
1665
1666 let prefix_array: ArrayRef = Arc::new(Int32Array::from(vec![1, 2, 3, 1]));
1667 let mut expected_hashes = vec![0; prefix_array.len()];
1668 create_hashes_with_hasher([&prefix_array], &hash_builder, &mut expected_hashes)
1669 .unwrap();
1670 for (hash, value) in expected_hashes.iter_mut().zip(&values) {
1671 if let Some(value) = value {
1672 *hash = combine_hashes(hash_builder.hash_one(value.as_bytes()), *hash);
1673 }
1674 }
1675
1676 let mut view_hashes = vec![0; view_array.len()];
1677 create_hashes_with_hasher(
1678 [&prefix_array, &view_array],
1679 &hash_builder,
1680 &mut view_hashes,
1681 )
1682 .unwrap();
1683 assert_eq!(view_hashes, expected_hashes);
1684 }
1685
1686 #[test]
1687 #[cfg(not(feature = "force_hash_collisions"))]
1688 fn test_single_column_leaf_hashes_match_with_same_hasher() {
1689 let arrays: Vec<ArrayRef> = vec![
1690 Arc::new(Int32Array::from(vec![Some(1), None, Some(-1)])),
1691 Arc::new(Float64Array::from(vec![Some(0.0), Some(-0.0), None])),
1692 Arc::new(StringArray::from(vec![Some("foo"), None, Some("bar")])),
1693 Arc::new(BinaryArray::from(vec![
1694 Some(&b"short"[..]),
1695 None,
1696 Some(&b"longer than twelve bytes"[..]),
1697 ])),
1698 Arc::new(StringViewArray::from(vec![
1699 Some("short"),
1700 None,
1701 Some("longer than twelve bytes"),
1702 ])),
1703 ];
1704 let random_state = RandomState::with_seed(0);
1705
1706 for array in arrays {
1707 let mut default_hashes = vec![0; array.len()];
1708 create_hashes([&array], &random_state, &mut default_hashes).unwrap();
1709
1710 let mut custom_hashes = vec![0; array.len()];
1711 create_hashes_with_hasher([&array], &random_state, &mut custom_hashes)
1712 .unwrap();
1713
1714 assert_eq!(
1715 custom_hashes,
1716 default_hashes,
1717 "single-column parity failed for {}",
1718 array.data_type()
1719 );
1720 }
1721 }
1722
1723 #[test]
1724 #[cfg(not(feature = "force_hash_collisions"))]
1725 fn test_with_hashes_with_custom_hasher() {
1726 let int_array: ArrayRef = Arc::new(Int32Array::from(vec![1, 2, 3]));
1727 let str_array: ArrayRef = Arc::new(StringArray::from(vec!["a", "b", "c"]));
1728 let hash_builder = BuildHasherDefault::<TestHasher>::default();
1729
1730 let mut expected_hashes = vec![0; int_array.len()];
1731 create_hashes_with_hasher(
1732 [&int_array, &str_array],
1733 &hash_builder,
1734 &mut expected_hashes,
1735 )
1736 .unwrap();
1737
1738 let actual_hashes =
1739 with_hashes_with_hasher([&int_array, &str_array], &hash_builder, |hashes| {
1740 Ok(hashes.to_vec())
1741 })
1742 .unwrap();
1743
1744 assert_eq!(actual_hashes, expected_hashes);
1745 }
1746
1747 #[test]
1748 #[cfg(not(feature = "force_hash_collisions"))]
1750 fn create_hashes_for_dict_arrays() {
1751 let strings = [Some("foo"), None, Some("bar"), Some("foo"), None];
1752
1753 let string_array: ArrayRef =
1754 Arc::new(strings.iter().cloned().collect::<StringArray>());
1755 let dict_array: ArrayRef = Arc::new(
1756 strings
1757 .iter()
1758 .cloned()
1759 .collect::<DictionaryArray<Int8Type>>(),
1760 );
1761
1762 let random_state = RandomState::with_seed(0);
1763
1764 let mut string_hashes = vec![0; strings.len()];
1765 create_hashes(&[string_array], &random_state, &mut string_hashes).unwrap();
1766
1767 let mut dict_hashes = vec![0; strings.len()];
1768 create_hashes(&[dict_array], &random_state, &mut dict_hashes).unwrap();
1769
1770 for (val, hash) in strings.iter().zip(string_hashes.iter()) {
1772 match val {
1773 Some(_) => assert_ne!(*hash, 0),
1774 None => assert_eq!(*hash, 0),
1775 }
1776 }
1777
1778 assert_eq!(string_hashes, dict_hashes);
1780
1781 assert_eq!(strings[1], strings[4]);
1783 assert_eq!(dict_hashes[1], dict_hashes[4]);
1784 assert_eq!(strings[0], strings[3]);
1785 assert_eq!(dict_hashes[0], dict_hashes[3]);
1786
1787 assert_ne!(strings[0], strings[2]);
1789 assert_ne!(dict_hashes[0], dict_hashes[2]);
1790 }
1791
1792 #[test]
1793 #[cfg(not(feature = "force_hash_collisions"))]
1795 fn create_hashes_for_list_arrays() {
1796 let data = vec![
1797 Some(vec![Some(0), Some(1), Some(2)]),
1798 None,
1799 Some(vec![Some(3), None, Some(5)]),
1800 Some(vec![Some(3), None, Some(5)]),
1801 None,
1802 Some(vec![Some(0), Some(1), Some(2)]),
1803 Some(vec![]),
1804 ];
1805 let list_array =
1806 Arc::new(ListArray::from_iter_primitive::<Int32Type, _, _>(data)) as ArrayRef;
1807 let random_state = RandomState::with_seed(0);
1808 let mut hashes = vec![0; list_array.len()];
1809 create_hashes(&[list_array], &random_state, &mut hashes).unwrap();
1810 assert_eq!(hashes[0], hashes[5]);
1811 assert_eq!(hashes[1], hashes[4]);
1812 assert_eq!(hashes[2], hashes[3]);
1813 assert_eq!(hashes[1], hashes[6]); }
1815
1816 #[test]
1817 #[cfg(not(feature = "force_hash_collisions"))]
1818 fn create_hashes_for_sliced_list_arrays() {
1819 let data = vec![
1820 Some(vec![Some(0), Some(1), Some(2)]),
1821 None,
1822 Some(vec![Some(3), None, Some(5)]),
1824 Some(vec![Some(3), None, Some(5)]),
1825 None,
1826 Some(vec![Some(0), Some(1), Some(2)]),
1828 Some(vec![]),
1829 ];
1830 let list_array =
1831 Arc::new(ListArray::from_iter_primitive::<Int32Type, _, _>(data)) as ArrayRef;
1832 let list_array = list_array.slice(2, 3);
1833 let random_state = RandomState::with_seed(0);
1834 let mut hashes = vec![0; list_array.len()];
1835 create_hashes(&[list_array], &random_state, &mut hashes).unwrap();
1836 assert_eq!(hashes[0], hashes[1]);
1837 assert_ne!(hashes[1], hashes[2]);
1838 }
1839
1840 #[test]
1841 #[cfg(not(feature = "force_hash_collisions"))]
1843 fn create_hashes_for_list_view_arrays() {
1844 use arrow::buffer::{NullBuffer, ScalarBuffer};
1845
1846 let values = Arc::new(Int32Array::from(vec![
1848 Some(0),
1849 Some(1),
1850 Some(2),
1851 Some(3),
1852 None,
1853 Some(5),
1854 ])) as ArrayRef;
1855 let field = Arc::new(Field::new("item", DataType::Int32, true));
1856
1857 let offsets = ScalarBuffer::from(vec![0i32, 0, 3, 3, 0, 0, 0]);
1866 let sizes = ScalarBuffer::from(vec![3i32, 0, 3, 3, 0, 3, 0]);
1867 let nulls = Some(NullBuffer::from(vec![
1868 true, false, true, true, false, true, true,
1869 ]));
1870
1871 let list_view_array =
1872 Arc::new(ListViewArray::new(field, offsets, sizes, values, nulls))
1873 as ArrayRef;
1874
1875 let random_state = RandomState::with_seed(0);
1876 let mut hashes = vec![0; list_view_array.len()];
1877 create_hashes(&[list_view_array], &random_state, &mut hashes).unwrap();
1878
1879 assert_eq!(hashes[0], hashes[5]); assert_eq!(hashes[1], hashes[4]); assert_eq!(hashes[2], hashes[3]); assert_eq!(hashes[1], hashes[6]); assert_ne!(hashes[0], hashes[2]); assert_ne!(hashes[0], hashes[6]); assert_ne!(hashes[2], hashes[6]); }
1889
1890 #[test]
1891 #[cfg(not(feature = "force_hash_collisions"))]
1893 fn create_hashes_for_large_list_view_arrays() {
1894 use arrow::buffer::{NullBuffer, ScalarBuffer};
1895
1896 let values = Arc::new(Int32Array::from(vec![
1898 Some(0),
1899 Some(1),
1900 Some(2),
1901 Some(3),
1902 None,
1903 Some(5),
1904 ])) as ArrayRef;
1905 let field = Arc::new(Field::new("item", DataType::Int32, true));
1906
1907 let offsets = ScalarBuffer::from(vec![0i64, 0, 3, 3, 0, 0, 0]);
1916 let sizes = ScalarBuffer::from(vec![3i64, 0, 3, 3, 0, 3, 0]);
1917 let nulls = Some(NullBuffer::from(vec![
1918 true, false, true, true, false, true, true,
1919 ]));
1920
1921 let large_list_view_array = Arc::new(LargeListViewArray::new(
1922 field, offsets, sizes, values, nulls,
1923 )) as ArrayRef;
1924
1925 let random_state = RandomState::with_seed(0);
1926 let mut hashes = vec![0; large_list_view_array.len()];
1927 create_hashes(&[large_list_view_array], &random_state, &mut hashes).unwrap();
1928
1929 assert_eq!(hashes[0], hashes[5]); assert_eq!(hashes[1], hashes[4]); assert_eq!(hashes[2], hashes[3]); assert_eq!(hashes[1], hashes[6]); assert_ne!(hashes[0], hashes[2]); assert_ne!(hashes[0], hashes[6]); assert_ne!(hashes[2], hashes[6]); }
1939
1940 #[test]
1941 #[cfg(not(feature = "force_hash_collisions"))]
1943 fn create_hashes_for_fixed_size_list_arrays() {
1944 let data = vec![
1945 Some(vec![Some(0), Some(1), Some(2)]),
1946 None,
1947 Some(vec![Some(3), None, Some(5)]),
1948 Some(vec![Some(3), None, Some(5)]),
1949 None,
1950 Some(vec![Some(0), Some(1), Some(2)]),
1951 ];
1952 let list_array =
1953 Arc::new(FixedSizeListArray::from_iter_primitive::<Int32Type, _, _>(
1954 data, 3,
1955 )) as ArrayRef;
1956 let random_state = RandomState::with_seed(0);
1957 let mut hashes = vec![0; list_array.len()];
1958 create_hashes(&[list_array], &random_state, &mut hashes).unwrap();
1959 assert_eq!(hashes[0], hashes[5]);
1960 assert_eq!(hashes[1], hashes[4]);
1961 assert_eq!(hashes[2], hashes[3]);
1962 }
1963
1964 #[test]
1965 #[cfg(not(feature = "force_hash_collisions"))]
1967 fn create_hashes_for_struct_arrays() {
1968 use arrow::buffer::Buffer;
1969
1970 let boolarr = Arc::new(BooleanArray::from(vec![
1971 false, false, true, true, true, true,
1972 ]));
1973 let i32arr = Arc::new(Int32Array::from(vec![10, 10, 20, 20, 30, 31]));
1974
1975 let struct_array = StructArray::from((
1976 vec![
1977 (
1978 Arc::new(Field::new("bool", DataType::Boolean, false)),
1979 Arc::clone(&boolarr) as ArrayRef,
1980 ),
1981 (
1982 Arc::new(Field::new("i32", DataType::Int32, false)),
1983 Arc::clone(&i32arr) as ArrayRef,
1984 ),
1985 (
1986 Arc::new(Field::new("i32", DataType::Int32, false)),
1987 Arc::clone(&i32arr) as ArrayRef,
1988 ),
1989 (
1990 Arc::new(Field::new("bool", DataType::Boolean, false)),
1991 Arc::clone(&boolarr) as ArrayRef,
1992 ),
1993 ],
1994 Buffer::from(&[0b001011]),
1995 ));
1996
1997 assert!(struct_array.is_valid(0));
1998 assert!(struct_array.is_valid(1));
1999 assert!(struct_array.is_null(2));
2000 assert!(struct_array.is_valid(3));
2001 assert!(struct_array.is_null(4));
2002 assert!(struct_array.is_null(5));
2003
2004 let array = Arc::new(struct_array) as ArrayRef;
2005
2006 let random_state = RandomState::with_seed(0);
2007 let mut hashes = vec![0; array.len()];
2008 create_hashes(&[array], &random_state, &mut hashes).unwrap();
2009 assert_eq!(hashes[0], hashes[1]);
2010 assert_ne!(hashes[2], hashes[3]);
2012 assert_eq!(hashes[4], hashes[5]);
2014 }
2015
2016 #[test]
2017 #[cfg(not(feature = "force_hash_collisions"))]
2019 fn create_hashes_for_struct_arrays_more_column_than_row() {
2020 let struct_array = StructArray::from(vec![
2021 (
2022 Arc::new(Field::new("bool", DataType::Boolean, false)),
2023 Arc::new(BooleanArray::from(vec![false, false])) as ArrayRef,
2024 ),
2025 (
2026 Arc::new(Field::new("i32-1", DataType::Int32, false)),
2027 Arc::new(Int32Array::from(vec![10, 10])) as ArrayRef,
2028 ),
2029 (
2030 Arc::new(Field::new("i32-2", DataType::Int32, false)),
2031 Arc::new(Int32Array::from(vec![10, 10])) as ArrayRef,
2032 ),
2033 (
2034 Arc::new(Field::new("i32-3", DataType::Int32, false)),
2035 Arc::new(Int32Array::from(vec![10, 10])) as ArrayRef,
2036 ),
2037 ]);
2038
2039 assert!(struct_array.is_valid(0));
2040 assert!(struct_array.is_valid(1));
2041
2042 let array = Arc::new(struct_array) as ArrayRef;
2043 let random_state = RandomState::with_seed(0);
2044 let mut hashes = vec![0; array.len()];
2045 create_hashes(&[array], &random_state, &mut hashes).unwrap();
2046 assert_eq!(hashes[0], hashes[1]);
2047 }
2048
2049 #[test]
2050 #[cfg(not(feature = "force_hash_collisions"))]
2052 fn create_hashes_for_map_arrays() {
2053 let mut builder =
2054 MapBuilder::new(None, StringBuilder::new(), Int32Builder::new());
2055 builder.keys().append_value("key1");
2057 builder.keys().append_value("key2");
2058 builder.values().append_value(1);
2059 builder.values().append_value(2);
2060 builder.append(true).unwrap();
2061 builder.keys().append_value("key1");
2063 builder.keys().append_value("key2");
2064 builder.values().append_value(1);
2065 builder.values().append_value(2);
2066 builder.append(true).unwrap();
2067 builder.keys().append_value("key1");
2069 builder.keys().append_value("key2");
2070 builder.values().append_value(1);
2071 builder.values().append_value(3);
2072 builder.append(true).unwrap();
2073 builder.keys().append_value("key1");
2075 builder.keys().append_value("key3");
2076 builder.values().append_value(1);
2077 builder.values().append_value(2);
2078 builder.append(true).unwrap();
2079 builder.keys().append_value("key1");
2081 builder.values().append_value(1);
2082 builder.append(true).unwrap();
2083 builder.keys().append_value("key1");
2085 builder.values().append_null();
2086 builder.append(true).unwrap();
2087 builder.append(true).unwrap();
2089 builder.keys().append_value("key1");
2091 builder.values().append_value(1);
2092 builder.append(false).unwrap();
2093
2094 let array = Arc::new(builder.finish()) as ArrayRef;
2095
2096 let random_state = RandomState::with_seed(0);
2097 let mut hashes = vec![0; array.len()];
2098 create_hashes(&[array], &random_state, &mut hashes).unwrap();
2099 assert_eq!(hashes[0], hashes[1]); assert_ne!(hashes[0], hashes[2]); assert_ne!(hashes[0], hashes[3]); assert_ne!(hashes[0], hashes[4]); assert_ne!(hashes[4], hashes[5]); assert_eq!(hashes[6], hashes[7]); }
2106
2107 #[test]
2108 #[cfg(not(feature = "force_hash_collisions"))]
2110 fn create_multi_column_hash_for_dict_arrays() {
2111 let strings1 = [Some("foo"), None, Some("bar")];
2112 let strings2 = [Some("blarg"), Some("blah"), None];
2113
2114 let string_array: ArrayRef =
2115 Arc::new(strings1.iter().cloned().collect::<StringArray>());
2116 let dict_array: ArrayRef = Arc::new(
2117 strings2
2118 .iter()
2119 .cloned()
2120 .collect::<DictionaryArray<Int32Type>>(),
2121 );
2122
2123 let random_state = RandomState::with_seed(0);
2124
2125 let mut one_col_hashes = vec![0; strings1.len()];
2126 create_hashes(
2127 &[Arc::clone(&dict_array) as ArrayRef],
2128 &random_state,
2129 &mut one_col_hashes,
2130 )
2131 .unwrap();
2132
2133 let mut two_col_hashes = vec![0; strings1.len()];
2134 create_hashes(
2135 &[dict_array, string_array],
2136 &random_state,
2137 &mut two_col_hashes,
2138 )
2139 .unwrap();
2140
2141 assert_eq!(one_col_hashes.len(), 3);
2142 assert_eq!(two_col_hashes.len(), 3);
2143
2144 assert_ne!(one_col_hashes, two_col_hashes);
2145 }
2146
2147 #[test]
2148 fn test_create_hashes_from_arrays() {
2149 let int_array: ArrayRef = Arc::new(Int32Array::from(vec![1, 2, 3, 4]));
2150 let float_array: ArrayRef =
2151 Arc::new(Float64Array::from(vec![1.0, 2.0, 3.0, 4.0]));
2152
2153 let random_state = RandomState::with_seed(0);
2154 let hashes_buff = &mut vec![0; int_array.len()];
2155 let hashes =
2156 create_hashes(&[int_array, float_array], &random_state, hashes_buff).unwrap();
2157 assert_eq!(hashes.len(), 4,);
2158 }
2159
2160 #[test]
2161 fn test_create_hashes_from_dyn_arrays() {
2162 let int_array: ArrayRef = Arc::new(Int32Array::from(vec![1, 2, 3, 4]));
2163 let float_array: ArrayRef =
2164 Arc::new(Float64Array::from(vec![1.0, 2.0, 3.0, 4.0]));
2165
2166 fn test(arr1: &dyn Array, arr2: &dyn Array) {
2168 let random_state = RandomState::with_seed(0);
2169 let hashes_buff = &mut vec![0; arr1.len()];
2170 let hashes = create_hashes([arr1, arr2], &random_state, hashes_buff).unwrap();
2171 assert_eq!(hashes.len(), 4,);
2172 }
2173 test(&*int_array, &*float_array);
2174 }
2175
2176 #[test]
2177 fn test_create_hashes_equivalence() {
2178 let array: ArrayRef = Arc::new(Int32Array::from(vec![1, 2, 3, 4]));
2179 let random_state = RandomState::with_seed(0);
2180
2181 let mut hashes1 = vec![0; array.len()];
2182 create_hashes(
2183 &[Arc::clone(&array) as ArrayRef],
2184 &random_state,
2185 &mut hashes1,
2186 )
2187 .unwrap();
2188
2189 let mut hashes2 = vec![0; array.len()];
2190 create_hashes([array], &random_state, &mut hashes2).unwrap();
2191
2192 assert_eq!(hashes1, hashes2);
2193 }
2194
2195 #[test]
2196 #[cfg(not(feature = "force_hash_collisions"))]
2197 fn test_create_hashes_with_quality_hash_state() {
2198 let int_array: ArrayRef = Arc::new(Int32Array::from(vec![1, 2, 3, 4]));
2199 let str_array: ArrayRef = Arc::new(StringArray::from(vec!["a", "b", "c", "d"]));
2200 let quality_state = foldhash::quality::FixedState::with_seed(0);
2201
2202 let mut one_col_hashes = vec![0; int_array.len()];
2203 create_hashes([&int_array], &quality_state, &mut one_col_hashes).unwrap();
2204 let expected_hashes: Vec<_> = [1i32, 2, 3, 4]
2205 .iter()
2206 .map(|value| quality_state.hash_one(value))
2207 .collect();
2208 assert_eq!(one_col_hashes, expected_hashes);
2209
2210 let mut two_col_hashes = vec![0; int_array.len()];
2211 create_hashes(
2212 [&int_array, &str_array],
2213 &quality_state,
2214 &mut two_col_hashes,
2215 )
2216 .unwrap();
2217 assert_ne!(two_col_hashes, one_col_hashes);
2218 }
2219
2220 #[test]
2221 fn test_with_hashes() {
2222 let array: ArrayRef = Arc::new(Int32Array::from(vec![1, 2, 3, 4]));
2223 let random_state = RandomState::with_seed(0);
2224
2225 let mut expected_hashes = vec![0; array.len()];
2227 create_hashes([&array], &random_state, &mut expected_hashes).unwrap();
2228
2229 let result = with_hashes([&array], &random_state, |hashes| {
2230 assert_eq!(hashes.len(), 4);
2231 assert_eq!(hashes, &expected_hashes[..]);
2233 Ok(hashes.to_vec())
2235 })
2236 .unwrap();
2237
2238 assert_eq!(result, expected_hashes);
2240 }
2241
2242 #[test]
2243 fn test_with_hashes_multi_column() {
2244 let int_array: ArrayRef = Arc::new(Int32Array::from(vec![1, 2, 3]));
2245 let str_array: ArrayRef = Arc::new(StringArray::from(vec!["a", "b", "c"]));
2246 let random_state = RandomState::with_seed(0);
2247
2248 let mut expected_hashes = vec![0; int_array.len()];
2250 create_hashes(
2251 [&int_array, &str_array],
2252 &random_state,
2253 &mut expected_hashes,
2254 )
2255 .unwrap();
2256
2257 with_hashes([&int_array, &str_array], &random_state, |hashes| {
2258 assert_eq!(hashes.len(), 3);
2259 assert_eq!(hashes, &expected_hashes[..]);
2260 Ok(())
2261 })
2262 .unwrap();
2263 }
2264
2265 #[test]
2266 fn test_with_hashes_empty_arrays() {
2267 let random_state = RandomState::with_seed(0);
2268
2269 let empty: [&ArrayRef; 0] = [];
2271 let result = with_hashes(empty, &random_state, |_hashes| Ok(()));
2272
2273 assert!(result.is_err());
2274 assert!(
2275 result
2276 .unwrap_err()
2277 .to_string()
2278 .contains("requires at least one array")
2279 );
2280 }
2281
2282 #[test]
2283 fn test_with_hashes_reentrancy() {
2284 let array: ArrayRef = Arc::new(Int32Array::from(vec![1, 2, 3]));
2285 let array2: ArrayRef = Arc::new(Int32Array::from(vec![4, 5, 6]));
2286 let random_state = RandomState::with_seed(0);
2287
2288 let result = with_hashes([&array], &random_state, |_hashes| {
2290 with_hashes([&array2], &random_state, |_inner_hashes| Ok(()))
2292 });
2293
2294 assert!(result.is_err());
2295 let err_msg = result.unwrap_err().to_string();
2296 assert!(
2297 err_msg.contains("reentrantly") || err_msg.contains("cannot be called"),
2298 "Error message should mention reentrancy: {err_msg}",
2299 );
2300 }
2301
2302 #[test]
2303 #[cfg(not(feature = "force_hash_collisions"))]
2304 fn create_hashes_for_sparse_union_arrays() {
2305 let int_array = Int32Array::from(vec![Some(5), None, Some(10), Some(5)]);
2307 let str_array = StringArray::from(vec![None, Some("foo"), None, None]);
2308
2309 let type_ids = vec![0_i8, 1, 0, 0].into();
2310 let children = vec![
2311 Arc::new(int_array) as ArrayRef,
2312 Arc::new(str_array) as ArrayRef,
2313 ];
2314
2315 let union_fields = [
2316 (0, Arc::new(Field::new("a", DataType::Int32, true))),
2317 (1, Arc::new(Field::new("b", DataType::Utf8, true))),
2318 ]
2319 .into_iter()
2320 .collect();
2321
2322 let array = UnionArray::try_new(union_fields, type_ids, None, children).unwrap();
2323 let array_ref = Arc::new(array) as ArrayRef;
2324
2325 let random_state = RandomState::with_seed(0);
2326 let mut hashes = vec![0; array_ref.len()];
2327 create_hashes(&[array_ref], &random_state, &mut hashes).unwrap();
2328
2329 assert_eq!(hashes[0], hashes[3]);
2331 assert_ne!(hashes[0], hashes[2]);
2333 assert_ne!(hashes[0], hashes[1]);
2335 }
2336
2337 #[test]
2338 #[cfg(not(feature = "force_hash_collisions"))]
2339 fn create_hashes_for_sparse_union_arrays_with_nulls() {
2340 let int_array = Int32Array::from(vec![Some(5), None, None, None]);
2342 let str_array = StringArray::from(vec![None, Some("foo"), None, None]);
2343
2344 let type_ids = vec![0, 1, 0, 1].into();
2345 let children = vec![
2346 Arc::new(int_array) as ArrayRef,
2347 Arc::new(str_array) as ArrayRef,
2348 ];
2349
2350 let union_fields = [
2351 (0, Arc::new(Field::new("a", DataType::Int32, true))),
2352 (1, Arc::new(Field::new("b", DataType::Utf8, true))),
2353 ]
2354 .into_iter()
2355 .collect();
2356
2357 let array = UnionArray::try_new(union_fields, type_ids, None, children).unwrap();
2358 let array_ref = Arc::new(array) as ArrayRef;
2359
2360 let random_state = RandomState::with_seed(0);
2361 let mut hashes = vec![0; array_ref.len()];
2362 create_hashes(&[array_ref], &random_state, &mut hashes).unwrap();
2363
2364 assert_eq!(hashes[2], hashes[3]);
2367
2368 assert_ne!(hashes[0], hashes[2]);
2370
2371 assert_ne!(hashes[1], hashes[3]);
2373 }
2374
2375 #[test]
2376 #[cfg(not(feature = "force_hash_collisions"))]
2377 fn create_hashes_for_dense_union_arrays() {
2378 let int_array = Int32Array::from(vec![67, 100, 67]);
2381 let str_array = StringArray::from(vec!["norm", "macdonald"]);
2382
2383 let type_ids = vec![0, 1, 0, 1, 0].into();
2384 let offsets = vec![0, 0, 1, 1, 2].into();
2385 let children = vec![
2386 Arc::new(int_array) as ArrayRef,
2387 Arc::new(str_array) as ArrayRef,
2388 ];
2389
2390 let union_fields = [
2391 (0, Arc::new(Field::new("a", DataType::Int32, false))),
2392 (1, Arc::new(Field::new("b", DataType::Utf8, false))),
2393 ]
2394 .into_iter()
2395 .collect();
2396
2397 let array =
2398 UnionArray::try_new(union_fields, type_ids, Some(offsets), children).unwrap();
2399 let array_ref = Arc::new(array) as ArrayRef;
2400
2401 let random_state = RandomState::with_seed(0);
2402 let mut hashes = vec![0; array_ref.len()];
2403 create_hashes(&[array_ref], &random_state, &mut hashes).unwrap();
2404
2405 assert_ne!(hashes[0], hashes[1]);
2407 assert_ne!(hashes[0], hashes[2]);
2409 assert_ne!(hashes[1], hashes[3]);
2411 assert_ne!(hashes[2], hashes[3]);
2413 assert_eq!(hashes[0], hashes[4]);
2415 }
2416
2417 #[test]
2418 #[cfg(not(feature = "force_hash_collisions"))]
2419 fn create_hashes_for_sliced_run_array() -> Result<()> {
2420 let values = Arc::new(Int32Array::from(vec![10, 20, 30]));
2421 let run_ends = Arc::new(Int32Array::from(vec![2, 5, 7]));
2422 let array = Arc::new(RunArray::try_new(&run_ends, values.as_ref()).unwrap());
2423
2424 let random_state = RandomState::with_seed(0);
2425 let mut full_hashes = vec![0; array.len()];
2426 create_hashes(
2427 &[Arc::clone(&array) as ArrayRef],
2428 &random_state,
2429 &mut full_hashes,
2430 )?;
2431
2432 let array_ref: ArrayRef = Arc::clone(&array) as ArrayRef;
2433 let sliced_array = array_ref.slice(2, 3);
2434
2435 let mut sliced_hashes = vec![0; sliced_array.len()];
2436 create_hashes(
2437 std::slice::from_ref(&sliced_array),
2438 &random_state,
2439 &mut sliced_hashes,
2440 )?;
2441
2442 assert_eq!(sliced_hashes.len(), 3);
2443 assert_eq!(sliced_hashes[0], sliced_hashes[1]);
2444 assert_eq!(sliced_hashes[1], sliced_hashes[2]);
2445 assert_eq!(&sliced_hashes, &full_hashes[2..5]);
2446
2447 Ok(())
2448 }
2449
2450 #[test]
2451 #[cfg(not(feature = "force_hash_collisions"))]
2452 fn test_run_array_with_nulls() -> Result<()> {
2453 let values = Arc::new(Int32Array::from(vec![Some(10), None, Some(20)]));
2454 let run_ends = Arc::new(Int32Array::from(vec![2, 4, 6]));
2455 let array = Arc::new(RunArray::try_new(&run_ends, values.as_ref()).unwrap());
2456
2457 let random_state = RandomState::with_seed(0);
2458 let mut hashes = vec![0; array.len()];
2459 create_hashes(
2460 &[Arc::clone(&array) as ArrayRef],
2461 &random_state,
2462 &mut hashes,
2463 )?;
2464
2465 assert_eq!(hashes[0], hashes[1]);
2466 assert_ne!(hashes[0], 0);
2467 assert_eq!(hashes[2], hashes[3]);
2468 assert_eq!(hashes[2], 0);
2469 assert_eq!(hashes[4], hashes[5]);
2470 assert_ne!(hashes[4], 0);
2471 assert_ne!(hashes[0], hashes[4]);
2472
2473 Ok(())
2474 }
2475
2476 #[test]
2477 #[cfg(not(feature = "force_hash_collisions"))]
2478 fn test_run_array_with_nulls_multicolumn() -> Result<()> {
2479 let primitive_array = Arc::new(Int32Array::from(vec![Some(10), None, Some(20)]));
2480 let run_values = Arc::new(Int32Array::from(vec![Some(10), None, Some(20)]));
2481 let run_ends = Arc::new(Int32Array::from(vec![1, 2, 3]));
2482 let run_array =
2483 Arc::new(RunArray::try_new(&run_ends, run_values.as_ref()).unwrap());
2484 let second_col = Arc::new(Int32Array::from(vec![100, 200, 300]));
2485
2486 let random_state = RandomState::with_seed(0);
2487
2488 let mut primitive_hashes = vec![0; 3];
2489 create_hashes(
2490 &[
2491 Arc::clone(&primitive_array) as ArrayRef,
2492 Arc::clone(&second_col) as ArrayRef,
2493 ],
2494 &random_state,
2495 &mut primitive_hashes,
2496 )?;
2497
2498 let mut run_hashes = vec![0; 3];
2499 create_hashes(
2500 &[
2501 Arc::clone(&run_array) as ArrayRef,
2502 Arc::clone(&second_col) as ArrayRef,
2503 ],
2504 &random_state,
2505 &mut run_hashes,
2506 )?;
2507
2508 assert_eq!(primitive_hashes, run_hashes);
2509
2510 Ok(())
2511 }
2512}