Skip to main content

vortex_array/scalar/
scalar_value.rs

1// SPDX-License-Identifier: Apache-2.0
2// SPDX-FileCopyrightText: Copyright the Vortex contributors
3
4//! Core [`ScalarValue`] type definition.
5
6use std::fmt::Display;
7use std::fmt::Formatter;
8
9use itertools::Itertools;
10use vortex_buffer::BufferString;
11use vortex_buffer::ByteBuffer;
12use vortex_error::VortexExpect;
13use vortex_error::vortex_panic;
14
15use crate::dtype::DType;
16use crate::scalar::DecimalValue;
17use crate::scalar::PValue;
18use crate::scalar::Scalar;
19use crate::scalar::UnionValue;
20
21/// The value stored in a [`Scalar`].
22///
23/// This enum represents the possible non-null values that can be stored in a scalar. When the
24/// scalar is null, the value is represented as `None` in the `Option<ScalarValue>` field.
25#[derive(Debug, Clone, PartialEq, Eq, Hash)]
26pub enum ScalarValue {
27    /// A boolean value.
28    Bool(bool),
29    /// A primitive numeric value.
30    Primitive(PValue),
31    /// A decimal value.
32    Decimal(DecimalValue),
33    /// A UTF-8 encoded string value.
34    Utf8(BufferString),
35    /// A binary (byte array) value.
36    Binary(ByteBuffer),
37    /// A tuple of potentially null scalar values.
38    ///
39    /// Used as the underlying representation for list, fixed-size list, map, and struct scalars.
40    Tuple(Vec<Option<ScalarValue>>),
41    /// A present union value carrying its selected type ID and raw child value.
42    Union(UnionValue),
43    /// A row-specific scalar wrapped by `DType::Variant`.
44    Variant(Box<Scalar>),
45}
46
47impl ScalarValue {
48    /// Returns the zero / identity value for the given [`DType`].
49    pub(super) fn zero_value(dtype: &DType) -> Self {
50        Self::try_zero_value(dtype)
51            .unwrap_or_else(|| vortex_panic!("{dtype} has no non-null zero value"))
52    }
53
54    /// Returns the non-null zero value for `dtype`, or [`None`] if no such value exists.
55    pub(super) fn try_zero_value(dtype: &DType) -> Option<Self> {
56        Some(match dtype {
57            DType::Null => return None,
58            DType::Bool(_) => Self::Bool(false),
59            DType::Primitive(ptype, _) => Self::Primitive(PValue::zero(ptype)),
60            DType::Decimal(dt, ..) => Self::Decimal(DecimalValue::zero(dt)),
61            DType::Utf8(_) => Self::Utf8(BufferString::empty()),
62            DType::Binary(_) => Self::Binary(ByteBuffer::empty()),
63            DType::List(..) => Self::Tuple(vec![]),
64            DType::Map(..) => Self::Tuple(vec![]),
65            DType::FixedSizeList(edt, size, _) => {
66                let elements = (0..*size)
67                    .map(|_| Self::try_zero_value(edt).map(Some))
68                    .collect::<Option<Vec<_>>>()?;
69                Self::Tuple(elements)
70            }
71            DType::Struct(fields, _) => {
72                let field_values = fields
73                    .fields()
74                    .map(|f| Self::try_zero_value(&f).map(Some))
75                    .collect::<Option<Vec<_>>>()?;
76                Self::Tuple(field_values)
77            }
78            DType::Union(variants, _) => {
79                let child_dtype = variants
80                    .variant_by_index(0)
81                    .vortex_expect("union must have at least one variant");
82                let child_value = Self::try_zero_value(&child_dtype)?;
83
84                Self::Union(UnionValue::new(
85                    variants.child_index_to_tag(0),
86                    Some(child_value),
87                ))
88            }
89            DType::Variant(_) => Self::Variant(Box::new(Scalar::null(DType::Null))),
90            DType::Extension(ext_dtype) => {
91                // Since we have no way to define a "zero" extension value (since we have no idea
92                // what the semantics of the extension is), a best effort attempt is to just use the
93                // zero storage value and try to make an extension scalar from that.
94                Self::try_zero_value(ext_dtype.storage_dtype())?
95            }
96        })
97    }
98
99    /// Returns a valid default value for `dtype`, or [`None`] if the dtype has no default.
100    ///
101    /// The outer [`Option`] distinguishes a dtype with no default from a nullable dtype, whose valid
102    /// default is represented by the inner [`None`].
103    pub(super) fn try_default_value(dtype: &DType) -> Option<Option<Self>> {
104        if dtype.is_nullable() {
105            return Some(None);
106        }
107
108        let value = match dtype {
109            DType::Null => return Some(None),
110            DType::Bool(_) => Self::Bool(false),
111            DType::Primitive(ptype, _) => Self::Primitive(PValue::zero(ptype)),
112            DType::Decimal(dt, ..) => Self::Decimal(DecimalValue::zero(dt)),
113            DType::Utf8(_) => Self::Utf8(BufferString::empty()),
114            DType::Binary(_) => Self::Binary(ByteBuffer::empty()),
115            DType::List(..) => Self::Tuple(vec![]),
116            DType::Map(..) => Self::Tuple(vec![]),
117            DType::FixedSizeList(edt, size, _) => {
118                let elements = (0..*size)
119                    .map(|_| Self::try_default_value(edt))
120                    .collect::<Option<Vec<_>>>()?;
121                Self::Tuple(elements)
122            }
123            DType::Struct(fields, _) => {
124                let field_values = fields
125                    .fields()
126                    .map(|field| Self::try_default_value(&field))
127                    .collect::<Option<Vec<_>>>()?;
128                Self::Tuple(field_values)
129            }
130            DType::Union(variants, _) => {
131                let child_dtype = variants
132                    .variant_by_index(0)
133                    .vortex_expect("union must have at least one variant");
134                let child_value = Self::try_default_value(&child_dtype)?;
135
136                Self::Union(UnionValue::new(variants.child_index_to_tag(0), child_value))
137            }
138            DType::Variant(_) => Self::Variant(Box::new(Scalar::null(DType::Null))),
139            DType::Extension(ext_dtype) => {
140                // Since we have no way to define a "default" extension value (since we have no idea
141                // what the semantics of the extension is), a best effort attempt is to just use the
142                // default storage value and try to make an extension scalar from that.
143                Self::try_default_value(ext_dtype.storage_dtype())??
144            }
145        };
146
147        Some(Some(value))
148    }
149}
150
151impl Display for ScalarValue {
152    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
153        match self {
154            ScalarValue::Bool(b) => write!(f, "{b}"),
155            ScalarValue::Primitive(p) => write!(f, "{p}"),
156            ScalarValue::Decimal(d) => write!(f, "{d}"),
157            ScalarValue::Utf8(s) => {
158                let bufstr = s.as_str();
159                let str_len = bufstr.chars().count();
160
161                if str_len > 10 {
162                    let prefix = String::from_iter(bufstr.chars().take(5));
163                    let suffix = String::from_iter(bufstr.chars().skip(str_len - 5));
164
165                    write!(f, "\"{prefix}..{suffix}\"")
166                } else {
167                    write!(f, "\"{bufstr}\"")
168                }
169            }
170            ScalarValue::Binary(b) => {
171                if b.len() > 10 {
172                    write!(
173                        f,
174                        "{}..{}",
175                        to_hex(&b[0..5]),
176                        to_hex(&b[b.len() - 5..b.len()]),
177                    )
178                } else {
179                    write!(f, "{}", to_hex(b))
180                }
181            }
182            ScalarValue::Tuple(elements) => {
183                write!(f, "[")?;
184                for (i, element) in elements.iter().enumerate() {
185                    if i > 0 {
186                        write!(f, ", ")?;
187                    }
188                    match element {
189                        None => write!(f, "null")?,
190                        Some(e) => write!(f, "{}", e)?,
191                    }
192                }
193                write!(f, "]")
194            }
195            ScalarValue::Union(value) => {
196                write!(f, "union@{}(", value.type_id())?;
197                match value.child_value() {
198                    Some(value) => write!(f, "{value}"),
199                    None => write!(f, "null"),
200                }?;
201                write!(f, ")")
202            }
203            ScalarValue::Variant(value) => write!(f, "{value}"),
204        }
205    }
206}
207
208/// Formats a byte slice as a hexadecimal string.
209fn to_hex(slice: &[u8]) -> String {
210    slice
211        .iter()
212        .format_with("", |f, b| b(&format_args!("{f:02x}")))
213        .to_string()
214}