33
44use std:: fmt:: Display ;
55use std:: fmt:: Formatter ;
6+ use std:: hash:: Hash ;
7+ use std:: hash:: Hasher ;
68use std:: sync:: Arc ;
79
810use DType :: * ;
@@ -144,6 +146,36 @@ impl DType {
144146 }
145147 }
146148
149+ /// Hash this dtype using the same equivalence relation as [`Self::eq_ignore_nullability`].
150+ pub ( crate ) fn hash_ignore_nullability < H : Hasher > ( & self , state : & mut H ) {
151+ std:: mem:: discriminant ( self ) . hash ( state) ;
152+
153+ match self {
154+ Null | Bool ( _) | Utf8 ( _) | Binary ( _) | Variant ( _) => { }
155+ Primitive ( ptype, _) => ptype. hash ( state) ,
156+ Decimal ( decimal, _) => decimal. hash ( state) ,
157+ List ( element, _) => element. hash_ignore_nullability ( state) ,
158+ FixedSizeList ( element, size, _) => {
159+ size. hash ( state) ;
160+ element. hash_ignore_nullability ( state) ;
161+ }
162+ Struct ( fields, _) => {
163+ fields. names ( ) . hash ( state) ;
164+ for field in fields. fields ( ) {
165+ field. hash_ignore_nullability ( state) ;
166+ }
167+ }
168+ Union ( variants) => {
169+ variants. names ( ) . hash ( state) ;
170+ variants. type_ids ( ) . hash ( state) ;
171+ for variant in variants. variants ( ) {
172+ variant. hash_ignore_nullability ( state) ;
173+ }
174+ }
175+ Extension ( ext) => ext. hash_ignore_nullability ( state) ,
176+ }
177+ }
178+
147179 /// Returns `true` if `self` is a subset type of `other, otherwise `false`.
148180 ///
149181 /// If `self` is nullable, this means that the other `DType` must also be nullable (since a
@@ -507,18 +539,29 @@ impl Display for DType {
507539
508540#[ cfg( test) ]
509541mod tests {
542+ use std:: collections:: hash_map:: DefaultHasher ;
543+ use std:: hash:: Hasher ;
510544 use std:: sync:: Arc ;
511545
546+ use vortex_error:: VortexResult ;
547+
512548 use crate :: dtype:: DType ;
513549 use crate :: dtype:: Nullability :: NonNullable ;
514550 use crate :: dtype:: Nullability :: Nullable ;
515551 use crate :: dtype:: PType ;
552+ use crate :: dtype:: UnionVariants ;
516553 use crate :: dtype:: decimal:: DecimalDType ;
517554 use crate :: extension:: datetime:: Date ;
518555 use crate :: extension:: datetime:: Time ;
519556 use crate :: extension:: datetime:: TimeUnit ;
520557 use crate :: extension:: datetime:: Timestamp ;
521558
559+ fn hash_ignore_nullability ( dtype : & DType ) -> u64 {
560+ let mut hasher = DefaultHasher :: new ( ) ;
561+ dtype. hash_ignore_nullability ( & mut hasher) ;
562+ hasher. finish ( )
563+ }
564+
522565 #[ test]
523566 fn test_ext_dtype_eq_ignore_nullability ( ) {
524567 let d1 = DType :: Extension ( Time :: new ( TimeUnit :: Seconds , Nullable ) . erased ( ) ) ;
@@ -534,6 +577,31 @@ mod tests {
534577 assert ! ( !t1. eq_ignore_nullability( & t2) ) ;
535578 }
536579
580+ #[ test]
581+ fn test_union_dtype_hash_ignores_variant_nullability ( ) -> VortexResult < ( ) > {
582+ let lhs = DType :: Union ( UnionVariants :: try_new (
583+ [ "int" , "string" ] . into ( ) ,
584+ vec ! [
585+ DType :: Primitive ( PType :: I32 , Nullable ) ,
586+ DType :: Utf8 ( NonNullable ) ,
587+ ] ,
588+ vec ! [ 5 , 9 ] ,
589+ ) ?) ;
590+ let rhs = DType :: Union ( UnionVariants :: try_new (
591+ [ "int" , "string" ] . into ( ) ,
592+ vec ! [
593+ DType :: Primitive ( PType :: I32 , NonNullable ) ,
594+ DType :: Utf8 ( Nullable ) ,
595+ ] ,
596+ vec ! [ 5 , 9 ] ,
597+ ) ?) ;
598+
599+ assert ! ( lhs. eq_ignore_nullability( & rhs) ) ;
600+ assert_eq ! ( hash_ignore_nullability( & lhs) , hash_ignore_nullability( & rhs) ) ;
601+
602+ Ok ( ( ) )
603+ }
604+
537605 #[ test]
538606 fn element_size_null ( ) {
539607 assert_eq ! ( DType :: Null . element_size( ) , Some ( 0 ) ) ;
0 commit comments