vortex_decimal_byte_parts/decimal_byte_parts/
mod.rs1use std::fmt::Display;
5use std::fmt::Formatter;
6use std::hash::Hasher;
7
8use vortex_array::Array;
9use vortex_array::ArrayParts;
10use vortex_array::ArrayView;
11pub(crate) mod compute;
12mod rules;
13mod slice;
14
15use prost::Message as _;
16use vortex_array::ArrayEq;
17use vortex_array::ArrayHash;
18use vortex_array::ArrayId;
19use vortex_array::ArrayRef;
20use vortex_array::EqMode;
21use vortex_array::ExecutionCtx;
22use vortex_array::ExecutionResult;
23use vortex_array::IntoArray;
24use vortex_array::array_slots;
25use vortex_array::arrays::DecimalArray;
26use vortex_array::arrays::PrimitiveArray;
27use vortex_array::buffer::BufferHandle;
28use vortex_array::dtype::DType;
29use vortex_array::dtype::DecimalDType;
30use vortex_array::dtype::PType;
31use vortex_array::match_each_signed_integer_ptype;
32use vortex_array::scalar::DecimalValue;
33use vortex_array::scalar::Scalar;
34use vortex_array::scalar::ScalarValue;
35use vortex_array::serde::ArrayChildren;
36use vortex_array::smallvec::smallvec;
37use vortex_array::vtable::OperationsVTable;
38use vortex_array::vtable::VTable;
39use vortex_array::vtable::ValidityChild;
40use vortex_array::vtable::ValidityVTableFromChild;
41use vortex_error::VortexExpect;
42use vortex_error::VortexResult;
43use vortex_error::vortex_bail;
44use vortex_error::vortex_ensure;
45use vortex_error::vortex_panic;
46use vortex_session::VortexSession;
47use vortex_session::registry::CachedId;
48
49use crate::decimal_byte_parts::rules::PARENT_RULES;
50
51pub type DecimalBytePartsArray = Array<DecimalByteParts>;
53
54impl ArrayHash for DecimalBytePartsData {
55 fn array_hash<H: Hasher>(&self, _state: &mut H, _accuracy: EqMode) {}
56}
57
58impl ArrayEq for DecimalBytePartsData {
59 fn array_eq(&self, _other: &Self, _accuracy: EqMode) -> bool {
60 true
61 }
62}
63
64#[derive(Clone, prost::Message)]
65pub struct DecimalBytesPartsMetadata {
66 #[prost(enumeration = "PType", tag = "1")]
67 zeroth_child_ptype: i32,
68 #[prost(uint32, tag = "2")]
69 lower_part_count: u32,
70}
71
72impl VTable for DecimalByteParts {
73 type TypedArrayData = DecimalBytePartsData;
74
75 type OperationsVTable = Self;
76 type ValidityVTable = ValidityVTableFromChild;
77
78 fn id(&self) -> ArrayId {
79 static ID: CachedId = CachedId::new("vortex.decimal_byte_parts");
80 *ID
81 }
82
83 fn validate(
84 &self,
85 _data: &Self::TypedArrayData,
86 dtype: &DType,
87 len: usize,
88 slots: &[Option<ArrayRef>],
89 ) -> VortexResult<()> {
90 let Some(decimal_dtype) = dtype.as_decimal_opt() else {
91 vortex_bail!("expected decimal dtype, got {}", dtype)
92 };
93 let msp = DecimalBytePartsSlotsView::from_slots(slots).msp;
94 DecimalBytePartsData::validate(msp, *decimal_dtype, dtype, len)
95 }
96
97 fn nbuffers(_array: ArrayView<'_, Self>) -> usize {
98 0
99 }
100
101 fn buffer(_array: ArrayView<'_, Self>, idx: usize) -> BufferHandle {
102 vortex_panic!("DecimalBytePartsArray buffer index {idx} out of bounds")
103 }
104
105 fn buffer_name(_array: ArrayView<'_, Self>, idx: usize) -> Option<String> {
106 vortex_panic!("DecimalBytePartsArray buffer_name index {idx} out of bounds")
107 }
108
109 fn with_buffers(
110 &self,
111 array: ArrayView<'_, Self>,
112 buffers: &[BufferHandle],
113 ) -> VortexResult<ArrayParts<Self>> {
114 vortex_array::vtable::with_empty_buffers(self, array, buffers)
115 }
116
117 fn serialize(
118 array: ArrayView<'_, Self>,
119 _session: &VortexSession,
120 ) -> VortexResult<Option<Vec<u8>>> {
121 Ok(Some(
122 DecimalBytesPartsMetadata {
123 zeroth_child_ptype: PType::try_from(array.msp().dtype())? as i32,
124 lower_part_count: 0,
125 }
126 .encode_to_vec(),
127 ))
128 }
129
130 fn deserialize(
131 &self,
132 dtype: &DType,
133 len: usize,
134 metadata: &[u8],
135 _buffers: &[BufferHandle],
136 children: &dyn ArrayChildren,
137 _session: &VortexSession,
138 ) -> VortexResult<ArrayParts<Self>> {
139 let metadata = DecimalBytesPartsMetadata::decode(metadata)?;
140 let Some(decimal_dtype) = dtype.as_decimal_opt() else {
141 vortex_bail!("decoding decimal but given non decimal dtype {}", dtype)
142 };
143
144 let encoded_dtype = DType::Primitive(metadata.zeroth_child_ptype(), dtype.nullability());
145
146 let msp = children.get(0, &encoded_dtype, len)?;
147
148 assert_eq!(
149 metadata.lower_part_count, 0,
150 "lower_part_count > 0 not currently supported"
151 );
152
153 let slots = smallvec![Some(msp.clone())];
154 let data = DecimalBytePartsData::try_new(msp.dtype(), msp.len(), *decimal_dtype)?;
155 Ok(ArrayParts::new(self.clone(), dtype.clone(), len, data).with_slots(slots))
156 }
157
158 fn slot_name(_array: ArrayView<'_, Self>, idx: usize) -> String {
159 DecimalBytePartsSlots::NAMES[idx].to_string()
160 }
161
162 fn reduce_parent(
163 array: ArrayView<'_, Self>,
164 parent: &ArrayRef,
165 child_idx: usize,
166 ) -> VortexResult<Option<ArrayRef>> {
167 PARENT_RULES.evaluate(array, parent, child_idx)
168 }
169
170 fn execute(array: Array<Self>, ctx: &mut ExecutionCtx) -> VortexResult<ExecutionResult> {
171 to_canonical_decimal(&array, ctx).map(ExecutionResult::done)
172 }
173}
174
175#[array_slots(DecimalByteParts)]
176pub struct DecimalBytePartsSlots {
177 pub msp: ArrayRef,
179}
180
181#[derive(Clone, Debug)]
187pub struct DecimalBytePartsData {
188 _lower_parts: Vec<ArrayRef>,
192}
193
194impl Display for DecimalBytePartsData {
195 fn fmt(&self, _f: &mut Formatter<'_>) -> std::fmt::Result {
196 Ok(())
197 }
198}
199
200pub struct DecimalBytePartsDataParts {
201 pub msp: ArrayRef,
202}
203
204impl DecimalBytePartsData {
205 pub fn validate(
206 msp: &ArrayRef,
207 decimal_dtype: DecimalDType,
208 dtype: &DType,
209 len: usize,
210 ) -> VortexResult<()> {
211 if !msp.dtype().is_signed_int() {
212 vortex_bail!("decimal bytes parts, first part must be a signed array")
213 }
214
215 let expected_dtype = DType::Decimal(decimal_dtype, msp.dtype().nullability());
216 vortex_ensure!(
217 dtype == &expected_dtype,
218 "expected dtype {expected_dtype}, got {dtype}"
219 );
220 vortex_ensure!(msp.len() == len, "expected len {len}, got {}", msp.len());
221 Ok(())
222 }
223
224 pub(crate) fn try_new(
225 msp_dtype: &DType,
226 msp_len: usize,
227 decimal_dtype: DecimalDType,
228 ) -> VortexResult<Self> {
229 let expected_dtype = DType::Decimal(decimal_dtype, msp_dtype.nullability());
230 vortex_ensure!(
231 msp_dtype.is_signed_int(),
232 "decimal bytes parts, first part must be a signed array"
233 );
234 let _ = msp_len;
235 drop(expected_dtype);
236 Ok(Self {
237 _lower_parts: Vec::new(),
238 })
239 }
240}
241
242#[derive(Clone, Debug)]
243pub struct DecimalByteParts;
244
245impl DecimalByteParts {
246 pub fn try_new(
248 msp: ArrayRef,
249 decimal_dtype: DecimalDType,
250 ) -> VortexResult<DecimalBytePartsArray> {
251 let len = msp.len();
252 let dtype = DType::Decimal(decimal_dtype, msp.dtype().nullability());
253 let slots = smallvec![Some(msp.clone())];
254 let data = DecimalBytePartsData::try_new(msp.dtype(), msp.len(), decimal_dtype)?;
255 Ok(unsafe {
256 Array::from_parts_unchecked(
257 ArrayParts::new(DecimalByteParts, dtype, len, data).with_slots(slots),
258 )
259 })
260 }
261}
262
263fn to_canonical_decimal(
265 array: &DecimalBytePartsArray,
266 ctx: &mut ExecutionCtx,
267) -> VortexResult<ArrayRef> {
268 let prim = array.msp().clone().execute::<PrimitiveArray>(ctx)?;
270 Ok(match_each_signed_integer_ptype!(prim.ptype(), |P| {
274 unsafe {
277 DecimalArray::new_unchecked(
278 prim.to_buffer::<P>(),
279 *array
280 .dtype()
281 .as_decimal_opt()
282 .vortex_expect("must be a decimal dtype"),
283 prim.validity()?,
284 )
285 }
286 .into_array()
287 }))
288}
289
290impl OperationsVTable<DecimalByteParts> for DecimalByteParts {
291 fn scalar_at(
292 array: ArrayView<'_, DecimalByteParts>,
293 index: usize,
294 ctx: &mut ExecutionCtx,
295 ) -> VortexResult<Scalar> {
296 let scalar = array.msp().execute_scalar(index, ctx)?;
298
299 let primitive_scalar = scalar.as_primitive();
301 let value = primitive_scalar.as_::<i64>().vortex_expect("non-null");
303 Scalar::try_new(
304 array.dtype().clone(),
305 Some(ScalarValue::Decimal(DecimalValue::I64(value))),
306 )
307 }
308}
309
310impl ValidityChild<DecimalByteParts> for DecimalByteParts {
311 fn validity_child(array: ArrayView<'_, DecimalByteParts>) -> ArrayRef {
312 array.msp().clone()
314 }
315}
316
317#[cfg(test)]
318mod tests {
319 use vortex_array::IntoArray;
320 use vortex_array::VortexSessionExecute;
321 use vortex_array::array_session;
322 use vortex_array::arrays::BoolArray;
323 use vortex_array::arrays::PrimitiveArray;
324 use vortex_array::dtype::DType;
325 use vortex_array::dtype::DecimalDType;
326 use vortex_array::dtype::Nullability;
327 use vortex_array::scalar::DecimalValue;
328 use vortex_array::scalar::Scalar;
329 use vortex_array::scalar::ScalarValue;
330 use vortex_array::validity::Validity;
331 use vortex_buffer::buffer;
332
333 use crate::DecimalByteParts;
334
335 #[test]
336 fn test_scalar_at_decimal_parts() {
337 let decimal_dtype = DecimalDType::new(8, 2);
338 let dtype = DType::Decimal(decimal_dtype, Nullability::Nullable);
339 let array = DecimalByteParts::try_new(
340 PrimitiveArray::new(
341 buffer![100i32, 200i32, 400i32],
342 Validity::Array(BoolArray::from_iter(vec![false, true, true]).into_array()),
343 )
344 .into_array(),
345 decimal_dtype,
346 )
347 .unwrap()
348 .into_array();
349
350 assert_eq!(
351 Scalar::null(dtype.clone()),
352 array
353 .execute_scalar(0, &mut array_session().create_execution_ctx())
354 .unwrap()
355 );
356 assert_eq!(
357 Scalar::try_new(
358 dtype.clone(),
359 Some(ScalarValue::Decimal(DecimalValue::I64(200)))
360 )
361 .unwrap(),
362 array
363 .execute_scalar(1, &mut array_session().create_execution_ctx())
364 .unwrap()
365 );
366 assert_eq!(
367 Scalar::try_new(dtype, Some(ScalarValue::Decimal(DecimalValue::I64(400)))).unwrap(),
368 array
369 .execute_scalar(2, &mut array_session().create_execution_ctx())
370 .unwrap()
371 );
372 }
373}