Skip to main content

vortex_btrblocks/schemes/
decimal.rs

1// SPDX-License-Identifier: Apache-2.0
2// SPDX-FileCopyrightText: Copyright the Vortex contributors
3
4//! Decimal compression scheme using byte-part decomposition.
5
6use vortex_array::ArrayId;
7use vortex_array::ArrayRef;
8use vortex_array::Canonical;
9use vortex_array::ExecutionCtx;
10use vortex_array::IntoArray;
11use vortex_array::VTable;
12use vortex_array::arrays::DecimalArray;
13use vortex_array::arrays::PrimitiveArray;
14use vortex_array::arrays::decimal::narrowed_decimal;
15use vortex_array::dtype::DecimalType;
16use vortex_compressor::scheme::CompressionEstimate;
17use vortex_compressor::scheme::EstimateVerdict;
18use vortex_decimal_byte_parts::DecimalByteParts;
19use vortex_error::VortexResult;
20
21use crate::ArrayAndStats;
22use crate::CascadingCompressor;
23use crate::CompressorContext;
24use crate::Scheme;
25use crate::SchemeExt;
26
27/// Compression scheme for decimal arrays via byte-part decomposition.
28///
29/// Narrows the decimal to the smallest integer type, compresses the underlying primitive, and wraps
30/// the result in a `DecimalBytePartsArray`.
31#[derive(Debug, Copy, Clone, PartialEq, Eq)]
32pub struct DecimalScheme;
33
34impl Scheme for DecimalScheme {
35    fn scheme_name(&self) -> &'static str {
36        "vortex.decimal.byte_parts"
37    }
38
39    fn matches(&self, canonical: &Canonical) -> bool {
40        matches!(canonical, Canonical::Decimal(_))
41    }
42
43    fn produced_encodings(&self) -> Vec<ArrayId> {
44        vec![DecimalByteParts.id()]
45    }
46
47    /// Children: primitive=0.
48    fn num_children(&self) -> usize {
49        1
50    }
51
52    fn expected_compression_ratio(
53        &self,
54        _data: &ArrayAndStats,
55        _compress_ctx: CompressorContext,
56        _exec_ctx: &mut ExecutionCtx,
57    ) -> CompressionEstimate {
58        // Decimal compression is almost always beneficial (narrowing + primitive compression).
59        CompressionEstimate::Verdict(EstimateVerdict::AlwaysUse)
60    }
61
62    fn compress(
63        &self,
64        compressor: &CascadingCompressor,
65        data: &ArrayAndStats,
66        compress_ctx: CompressorContext,
67        exec_ctx: &mut ExecutionCtx,
68    ) -> VortexResult<ArrayRef> {
69        // TODO(joe): add support splitting i128/256 buffers into chunks of primitive values
70        // for compression. 2 for i128 and 4 for i256.
71        let decimal = data.array().clone().execute::<DecimalArray>(exec_ctx)?;
72        let decimal = narrowed_decimal(decimal);
73        let validity = decimal.validity()?;
74        let prim = match decimal.values_type() {
75            DecimalType::I8 => PrimitiveArray::new(decimal.buffer::<i8>(), validity),
76            DecimalType::I16 => PrimitiveArray::new(decimal.buffer::<i16>(), validity),
77            DecimalType::I32 => PrimitiveArray::new(decimal.buffer::<i32>(), validity),
78            DecimalType::I64 => PrimitiveArray::new(decimal.buffer::<i64>(), validity),
79            _ => return Ok(decimal.into_array()),
80        };
81
82        let compressed =
83            compressor.compress_child(&prim.into_array(), &compress_ctx, self.id(), 0, exec_ctx)?;
84
85        DecimalByteParts::try_new(compressed, decimal.decimal_dtype()).map(|d| d.into_array())
86    }
87}