@@ -5,6 +5,7 @@ use vortex_array::ArrayRef;
55use vortex_array:: IntoArray ;
66use vortex_array:: builtins:: ArrayBuiltins ;
77use vortex_array:: dtype:: DType ;
8+ use vortex_array:: scalar:: Scalar ;
89use vortex_array:: scalar_fn:: fns:: cast:: CastReduce ;
910use vortex_error:: VortexResult ;
1011
@@ -13,8 +14,14 @@ use crate::SparseArray;
1314
1415impl CastReduce for Sparse {
1516 fn cast ( array : & SparseArray , dtype : & DType ) -> VortexResult < Option < ArrayRef > > {
16- // Cast both the patches values and the fill value
17- let casted_fill = array. fill_scalar ( ) . cast ( dtype) ?;
17+ let casted_fill = if array. patches ( ) . num_patches ( ) == array. len ( ) {
18+ // When every position is patched the fill is unused — skip casting it
19+ // entirely and substitute a zero value for the target dtype.
20+ Scalar :: zero_value ( dtype)
21+ } else {
22+ // Otherwise the cast must succeed.
23+ array. fill_scalar ( ) . cast ( dtype) ?
24+ } ;
1825 let casted_patches = array
1926 . patches ( )
2027 . clone ( )
@@ -114,4 +121,53 @@ mod tests {
114121 fn test_cast_sparse_conformance ( #[ case] array : SparseArray ) {
115122 test_cast_conformance ( & array. into_array ( ) ) ;
116123 }
124+
125+ #[ test]
126+ fn test_cast_sparse_null_fill_all_patched_to_non_nullable ( ) -> vortex_error:: VortexResult < ( ) > {
127+ // Regression test for https://github.com/vortex-data/vortex/issues/6932
128+ //
129+ // When all positions are patched the null fill is unused, so a cast to
130+ // non-nullable is valid. Sparse::cast detects this case, substitutes a
131+ // zero fill, and keeps the result in the Sparse encoding.
132+ let sparse = SparseArray :: try_new (
133+ buffer ! [ 0u64 , 1 , 2 , 3 , 4 ] . into_array ( ) ,
134+ buffer ! [ 10u64 , 20 , 30 , 40 , 50 ] . into_array ( ) ,
135+ 5 ,
136+ Scalar :: null_native :: < u64 > ( ) ,
137+ ) ?;
138+
139+ let casted = sparse
140+ . into_array ( )
141+ . cast ( DType :: Primitive ( PType :: U64 , Nullability :: NonNullable ) ) ?;
142+
143+ assert_eq ! (
144+ casted. dtype( ) ,
145+ & DType :: Primitive ( PType :: U64 , Nullability :: NonNullable )
146+ ) ;
147+
148+ let expected = PrimitiveArray :: from_iter ( [ 10u64 , 20 , 30 , 40 , 50 ] ) ;
149+ assert_arrays_eq ! ( casted. to_primitive( ) , expected) ;
150+ Ok ( ( ) )
151+ }
152+
153+ #[ test]
154+ fn test_fill_null_sparse_with_null_fill ( ) -> vortex_error:: VortexResult < ( ) > {
155+ // Regression test for https://github.com/vortex-data/vortex/issues/6932
156+ // fill_null on a sparse array with null fill triggers an internal cast to
157+ // non-nullable, which must not panic.
158+ let sparse = SparseArray :: try_new (
159+ buffer ! [ 1u64 , 3 ] . into_array ( ) ,
160+ buffer ! [ 10u64 , 20 ] . into_array ( ) ,
161+ 5 ,
162+ Scalar :: null_native :: < u64 > ( ) ,
163+ ) ?;
164+
165+ let filled = sparse. into_array ( ) . fill_null ( Scalar :: from ( 0u64 ) ) ?;
166+
167+ assert_eq ! (
168+ filled. dtype( ) ,
169+ & DType :: Primitive ( PType :: U64 , Nullability :: NonNullable )
170+ ) ;
171+ Ok ( ( ) )
172+ }
117173}
0 commit comments