44use std:: marker:: PhantomData ;
55
66use num_traits:: ToPrimitive ;
7+ use vortex:: array:: ArrayRef ;
8+ use vortex:: array:: VectorExecutor ;
79use vortex:: array:: arrays:: DecimalArray ;
810use vortex:: buffer:: Buffer ;
911use vortex:: dtype:: BigCast ;
@@ -15,6 +17,7 @@ use vortex::error::VortexResult;
1517use vortex:: error:: vortex_bail;
1618use vortex:: mask:: Mask ;
1719use vortex:: scalar:: DecimalType ;
20+ use vortex:: session:: VortexSession ;
1821
1922use crate :: duckdb:: Vector ;
2023use crate :: duckdb:: VectorBuffer ;
@@ -59,18 +62,6 @@ pub(crate) fn new_exporter(array: &DecimalArray) -> VortexResult<Box<dyn ColumnE
5962 } )
6063}
6164
62- /// Maps a decimal precision into the small type that can represent it.
63- /// see <https://duckdb.org/docs/stable/sql/data_types/numeric.html#fixed-point-decimals>
64- pub fn precision_to_duckdb_storage_size ( decimal_dtype : & DecimalDType ) -> VortexResult < DecimalType > {
65- Ok ( match decimal_dtype. precision ( ) {
66- 1 ..=4 => DecimalType :: I16 ,
67- 5 ..=9 => DecimalType :: I32 ,
68- 10 ..=18 => DecimalType :: I64 ,
69- 19 ..=38 => DecimalType :: I128 ,
70- decimal_dtype => vortex_bail ! ( "cannot represent decimal in ducdkb {decimal_dtype}" ) ,
71- } )
72- }
73-
7465impl < D : NativeDecimalType , N : NativeDecimalType > ColumnExporter for DecimalExporter < D , N >
7566where
7667 D : ToPrimitive ,
@@ -115,6 +106,112 @@ impl<D: NativeDecimalType> ColumnExporter for DecimalZeroCopyExporter<D> {
115106 }
116107}
117108
109+ struct DecimalVectorExporter < D : NativeDecimalType , N : NativeDecimalType > {
110+ values : Buffer < D > ,
111+ mask : Mask ,
112+ /// The DecimalType of the DuckDB column.
113+ dest_value_type : PhantomData < N > ,
114+ }
115+
116+ struct DecimalVectorZeroCopyExporter < D : NativeDecimalType > {
117+ len : usize ,
118+ begin : * const D ,
119+ shared_buffer : VectorBuffer ,
120+ mask : Mask ,
121+ }
122+
123+ pub ( crate ) fn new_vector_exporter (
124+ array : ArrayRef ,
125+ session : & VortexSession ,
126+ ) -> VortexResult < Box < dyn ColumnExporter > > {
127+ let vector = array. execute_vector ( session) ?. into_decimal ( ) ;
128+ let decimal_type = vector. decimal_type ( ) ;
129+
130+ let dest_values_type =
131+ precision_to_duckdb_storage_size ( & DecimalDType :: new ( vector. precision ( ) , vector. scale ( ) ) ) ?;
132+
133+ if decimal_type == dest_values_type {
134+ match_each_decimal_value_type ! ( decimal_type, |D | {
135+ let vector = D :: downcast( vector) ;
136+ let ( _, buffer, mask) = vector. into_parts( ) ;
137+ return Ok ( Box :: new( DecimalVectorZeroCopyExporter {
138+ len: buffer. len( ) ,
139+ begin: buffer. as_ptr( ) ,
140+ shared_buffer: VectorBuffer :: new( buffer) ,
141+ mask,
142+ } ) ) ;
143+ } )
144+ }
145+
146+ match_each_decimal_value_type ! ( decimal_type, |D | {
147+ match_each_decimal_value_type!( decimal_type, |N | {
148+ let vector = D :: downcast( vector) ;
149+ let ( _, buffer, mask) = vector. into_parts( ) ;
150+ Ok ( Box :: new( DecimalVectorExporter {
151+ values: buffer,
152+ mask,
153+ dest_value_type: PhantomData :: <N >,
154+ } ) )
155+ } )
156+ } )
157+ }
158+
159+ impl < D : NativeDecimalType , N : NativeDecimalType > ColumnExporter for DecimalVectorExporter < D , N >
160+ where
161+ D : ToPrimitive ,
162+ N : BigCast ,
163+ {
164+ fn export ( & self , offset : usize , len : usize , vector : & mut Vector ) -> VortexResult < ( ) > {
165+ // Set validity if necessary.
166+ if unsafe { vector. set_validity ( & self . mask , offset, len) } {
167+ // All values are null, so no point copying the data.
168+ return Ok ( ( ) ) ;
169+ }
170+
171+ // Copy the values from the Vortex array to the DuckDB vector.
172+ for ( src, dst) in self . values [ offset..offset + len]
173+ . iter ( )
174+ . zip ( unsafe { vector. as_slice_mut ( len) } )
175+ {
176+ * dst = <N as BigCast >:: from ( * src) . vortex_expect (
177+ "We know all decimals with this scale/precision fit into the target bit width" ,
178+ ) ;
179+ }
180+
181+ Ok ( ( ) )
182+ }
183+ }
184+
185+ impl < D : NativeDecimalType > ColumnExporter for DecimalVectorZeroCopyExporter < D > {
186+ fn export ( & self , offset : usize , len : usize , vector : & mut Vector ) -> VortexResult < ( ) > {
187+ if unsafe { vector. set_validity ( & self . mask , offset, len) } {
188+ // All values are null, so no point copying the data.
189+ return Ok ( ( ) ) ;
190+ }
191+
192+ assert ! ( self . len >= offset + len) ;
193+
194+ let pos = unsafe { self . begin . add ( offset) } ;
195+ unsafe { vector. set_vector_buffer ( & self . shared_buffer ) } ;
196+ // While we are setting a *mut T this is an artifact of the C API, this is in fact const.
197+ unsafe { vector. set_data_ptr ( pos as * mut D ) } ;
198+
199+ Ok ( ( ) )
200+ }
201+ }
202+
203+ /// Maps a decimal precision into the small type that can represent it.
204+ /// see <https://duckdb.org/docs/stable/sql/data_types/numeric.html#fixed-point-decimals>
205+ pub fn precision_to_duckdb_storage_size ( decimal_dtype : & DecimalDType ) -> VortexResult < DecimalType > {
206+ Ok ( match decimal_dtype. precision ( ) {
207+ 1 ..=4 => DecimalType :: I16 ,
208+ 5 ..=9 => DecimalType :: I32 ,
209+ 10 ..=18 => DecimalType :: I64 ,
210+ 19 ..=38 => DecimalType :: I128 ,
211+ decimal_dtype => vortex_bail ! ( "cannot represent decimal in ducdkb {decimal_dtype}" ) ,
212+ } )
213+ }
214+
118215#[ cfg( test) ]
119216mod tests {
120217 use vortex:: array:: arrays:: DecimalArray ;
0 commit comments