vortex_array/arrays/decimal/vtable/
mod.rs1use std::hash::Hasher;
5
6use prost::Message;
7use vortex_buffer::Alignment;
8use vortex_error::VortexResult;
9use vortex_error::vortex_bail;
10use vortex_error::vortex_ensure;
11use vortex_session::VortexSession;
12
13use crate::ArrayParts;
14use crate::ArrayRef;
15use crate::ExecutionCtx;
16use crate::ExecutionResult;
17use crate::array::Array;
18use crate::array::ArrayView;
19use crate::array::VTable;
20use crate::arrays::decimal::DecimalData;
21use crate::arrays::fixed_width::vtable as fixed_width;
22use crate::buffer::BufferHandle;
23use crate::builders::ArrayBuilder;
24use crate::builders::DecimalBuilder;
25use crate::dtype::DType;
26use crate::dtype::DecimalType;
27use crate::dtype::NativeDecimalType;
28use crate::match_each_decimal_value_type;
29use crate::serde::ArrayChildren;
30mod kernel;
31mod operations;
32mod validity;
33
34use std::hash::Hash;
35
36use vortex_session::registry::CachedId;
37
38use crate::EqMode;
39use crate::array::ArrayId;
40use crate::arrays::decimal::array::DecimalSlots;
41use crate::arrays::decimal::compute::rules::RULES;
42use crate::hash::ArrayEq;
43use crate::hash::ArrayHash;
44pub type DecimalArray = Array<Decimal>;
46
47pub(crate) fn initialize(session: &VortexSession) {
48 kernel::initialize(session);
49}
50
51#[derive(prost::Message)]
53pub struct DecimalMetadata {
54 #[prost(enumeration = "DecimalType", tag = "1")]
55 pub(super) values_type: i32,
56}
57
58impl ArrayHash for DecimalData {
59 fn array_hash<H: Hasher>(&self, state: &mut H, accuracy: EqMode) {
60 self.values.array_hash(state, accuracy);
61 std::mem::discriminant(&self.values_type).hash(state);
62 }
63}
64
65impl ArrayEq for DecimalData {
66 fn array_eq(&self, other: &Self, accuracy: EqMode) -> bool {
67 self.values.array_eq(&other.values, accuracy) && self.values_type == other.values_type
68 }
69}
70
71impl VTable for Decimal {
72 type TypedArrayData = DecimalData;
73
74 type OperationsVTable = Self;
75 type ValidityVTable = Self;
76
77 fn id(&self) -> ArrayId {
78 static ID: CachedId = CachedId::new("vortex.decimal");
79 *ID
80 }
81
82 fn nbuffers(_array: ArrayView<'_, Self>) -> usize {
83 1
84 }
85
86 fn buffer(array: ArrayView<'_, Self>, idx: usize) -> BufferHandle {
87 fixed_width::buffer("DecimalArray", &array.values, idx)
88 }
89
90 fn buffer_name(_array: ArrayView<'_, Self>, idx: usize) -> Option<String> {
91 fixed_width::buffer_name(idx)
92 }
93
94 fn with_buffers(
95 &self,
96 array: ArrayView<'_, Self>,
97 buffers: &[BufferHandle],
98 ) -> VortexResult<ArrayParts<Self>> {
99 let mut data = array.data().clone();
100 data.values = fixed_width::single_buffer(buffers)?;
101 Ok(
102 ArrayParts::new(self.clone(), array.dtype().clone(), array.len(), data)
103 .with_slots(array.slots().iter().cloned().collect()),
104 )
105 }
106
107 fn serialize(
108 array: ArrayView<'_, Self>,
109 _session: &VortexSession,
110 ) -> VortexResult<Option<Vec<u8>>> {
111 Ok(Some(
112 DecimalMetadata {
113 values_type: array.values_type() as i32,
114 }
115 .encode_to_vec(),
116 ))
117 }
118
119 fn validate(
120 &self,
121 data: &DecimalData,
122 dtype: &DType,
123 len: usize,
124 slots: &[Option<ArrayRef>],
125 ) -> VortexResult<()> {
126 let DType::Decimal(_, nullability) = dtype else {
127 vortex_bail!("Expected decimal dtype, got {dtype:?}");
128 };
129 vortex_ensure!(
130 data.len() == len,
131 InvalidArgument:
132 "DecimalArray length {} does not match outer length {}",
133 data.len(),
134 len
135 );
136 let validity =
137 crate::array::child_to_validity(slots[DecimalSlots::VALIDITY].as_ref(), *nullability);
138 if let Some(validity_len) = validity.maybe_len() {
139 vortex_ensure!(
140 validity_len == len,
141 InvalidArgument:
142 "DecimalArray validity len {} does not match outer length {}",
143 validity_len,
144 len
145 );
146 }
147
148 Ok(())
149 }
150
151 fn deserialize(
152 &self,
153 dtype: &DType,
154 len: usize,
155 metadata: &[u8],
156 buffers: &[BufferHandle],
157 children: &dyn ArrayChildren,
158 _session: &VortexSession,
159 ) -> VortexResult<ArrayParts<Self>> {
160 let metadata = DecimalMetadata::decode(metadata)?;
161 let values = fixed_width::single_buffer(buffers)?;
162
163 let validity = fixed_width::deserialize_validity(dtype.nullability(), len, children)?;
164
165 let Some(decimal_dtype) = dtype.as_decimal_opt() else {
166 vortex_bail!("Expected Decimal dtype, got {:?}", dtype)
167 };
168
169 let slots = DecimalData::make_slots(&validity, len);
170 let data = match_each_decimal_value_type!(metadata.values_type(), |D| {
171 vortex_ensure!(
173 values.is_aligned_to(Alignment::of::<D>()),
174 "DecimalArray buffer not aligned for values type {:?}",
175 D::DECIMAL_TYPE
176 );
177 DecimalData::try_new_handle(values, metadata.values_type(), *decimal_dtype)
178 })?;
179 Ok(ArrayParts::new(self.clone(), dtype.clone(), len, data).with_slots(slots))
180 }
181
182 fn slot_name(_array: ArrayView<'_, Self>, idx: usize) -> String {
183 DecimalSlots::NAMES[idx].to_string()
184 }
185
186 fn execute(array: Array<Self>, _ctx: &mut ExecutionCtx) -> VortexResult<ExecutionResult> {
187 Ok(ExecutionResult::done(array))
188 }
189
190 fn append_to_builder(
191 array: ArrayView<'_, Self>,
192 builder: &mut dyn ArrayBuilder,
193 ctx: &mut ExecutionCtx,
194 ) -> VortexResult<()> {
195 let Some(builder) = builder.as_any_mut().downcast_mut::<DecimalBuilder>() else {
196 vortex_bail!("append_to_builder for Decimal requires a DecimalBuilder");
197 };
198 builder.append_decimal_array(&array.into_owned(), ctx)
199 }
200
201 fn reduce_parent(
202 array: ArrayView<'_, Self>,
203 parent: &ArrayRef,
204 child_idx: usize,
205 ) -> VortexResult<Option<ArrayRef>> {
206 RULES.evaluate(array, parent, child_idx)
207 }
208}
209
210#[derive(Clone, Debug)]
211pub struct Decimal;
212
213#[cfg(test)]
214mod tests {
215 use vortex_buffer::ByteBufferMut;
216 use vortex_buffer::buffer;
217 use vortex_session::registry::ReadContext;
218
219 use crate::ArrayContext;
220 use crate::IntoArray;
221 use crate::VortexSessionExecute;
222 use crate::array_session;
223 use crate::arrays::Decimal;
224 use crate::arrays::DecimalArray;
225 use crate::assert_arrays_eq;
226 use crate::dtype::DecimalDType;
227 use crate::serde::SerializeOptions;
228 use crate::serde::SerializedArray;
229 use crate::validity::Validity;
230
231 #[test]
232 fn test_array_serde() {
233 let session = array_session();
234 let array = DecimalArray::new(
235 buffer![100i128, 200i128, 300i128, 400i128, 500i128],
236 DecimalDType::new(10, 2),
237 Validity::NonNullable,
238 );
239 let dtype = array.dtype().clone();
240
241 let array_ctx = ArrayContext::empty();
242 let out = array
243 .into_array()
244 .serialize(&array_ctx, &session, &SerializeOptions::default())
245 .unwrap();
246 let mut concat = ByteBufferMut::empty();
248 for buf in out {
249 concat.extend_from_slice(buf.as_ref());
250 }
251
252 let concat = concat.freeze();
253
254 let parts = SerializedArray::try_from(concat).unwrap();
255 let decoded = parts
256 .decode(&dtype, 5, &ReadContext::new(array_ctx.to_ids()), &session)
257 .unwrap();
258 assert!(decoded.is::<Decimal>());
259 }
260
261 #[test]
262 fn test_nullable_decimal_serde_roundtrip() {
263 let session = array_session();
264 let mut ctx = session.create_execution_ctx();
265 let array = DecimalArray::new(
266 buffer![1234567i32, 0i32, -9999999i32],
267 DecimalDType::new(7, 3),
268 Validity::from_iter([true, false, true]),
269 );
270 let dtype = array.dtype().clone();
271 let len = array.len();
272
273 let array_ctx = ArrayContext::empty();
274 let out = array
275 .clone()
276 .into_array()
277 .serialize(&array_ctx, &session, &SerializeOptions::default())
278 .unwrap();
279 let mut concat = ByteBufferMut::empty();
280 for buf in out {
281 concat.extend_from_slice(buf.as_ref());
282 }
283
284 let parts = SerializedArray::try_from(concat.freeze()).unwrap();
285 let decoded = parts
286 .decode(&dtype, len, &ReadContext::new(array_ctx.to_ids()), &session)
287 .unwrap();
288
289 assert_arrays_eq!(decoded, array, &mut ctx);
290 }
291}