Skip to content

Commit 346b1f6

Browse files
authored
Feature: add zero and null constructors for scalars (#5705)
I need this to help with implementing all of the `Cast` logic. Signed-off-by: Connor Tsui <[email protected]>
1 parent f8cab7a commit 346b1f6

File tree

13 files changed

+325
-46
lines changed

13 files changed

+325
-46
lines changed

vortex-buffer/src/string.rs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,11 @@ impl BufferString {
2323
Self(buffer)
2424
}
2525

26+
/// Creates an empty `BufferString`.
27+
pub fn empty() -> Self {
28+
Self(ByteBuffer::from(vec![]))
29+
}
30+
2631
/// Return a view of the contents of BufferString as an immutable `&str`.
2732
pub fn as_str(&self) -> &str {
2833
// SAFETY: We have already validated that the buffer is valid UTF-8

vortex-vector/src/binaryview/scalar.rs

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,10 +23,20 @@ impl<T: BinaryViewType> BinaryViewScalar<T> {
2323
}
2424

2525
impl<T: BinaryViewType> BinaryViewScalar<T> {
26-
/// Returns the scalar value as [`BinaryViewType::Scalar`], or `None` if the scalar is null.
26+
/// Returns the scalar value as [`BinaryViewType::Scalar`], or [`None`] if the scalar is null.
2727
pub fn value(&self) -> Option<&T::Scalar> {
2828
self.0.as_ref()
2929
}
30+
31+
/// Creates a zero (empty) binary view scalar.
32+
pub fn zero() -> Self {
33+
Self::new(Some(T::empty_scalar()))
34+
}
35+
36+
/// Creates a null binary view scalar.
37+
pub fn null() -> Self {
38+
Self::new(None)
39+
}
3040
}
3141

3242
impl<T: BinaryViewType> ScalarOps for BinaryViewScalar<T> {

vortex-vector/src/binaryview/types.rs

Lines changed: 27 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ use std::fmt::Debug;
77

88
use vortex_buffer::BufferString;
99
use vortex_buffer::ByteBuffer;
10+
use vortex_dtype::DType;
1011

1112
use crate::Vector;
1213
use crate::VectorMut;
@@ -60,9 +61,15 @@ pub trait BinaryViewType: Debug + Sized + Send + Sync + 'static + private::Seale
6061

6162
/// Upcast a type-specific instance to a generic instance.
6263
fn upcast<V: BinaryViewTypeUpcast>(input: V::Input<Self>) -> V;
64+
65+
/// Returns an empty scalar value.
66+
fn empty_scalar() -> Self::Scalar;
67+
68+
/// Returns true if the given dtype matches this binary view type.
69+
fn matches_dtype(dtype: &DType) -> bool;
6370
}
6471

65-
/// [`BinaryType`] for UTF-8 strings.
72+
/// [`BinaryViewType`] for UTF-8 strings.
6673
#[derive(Clone, Debug)]
6774
pub struct StringType;
6875
impl BinaryViewType for StringType {
@@ -82,7 +89,7 @@ impl BinaryViewType for StringType {
8289
}
8390

8491
#[inline(always)]
85-
unsafe fn scalar_from_buffer_unchecked(buffer: ByteBuffer) -> Self::Scalar {
92+
unsafe fn scalar_from_buffer_unchecked(buffer: ByteBuffer) -> BufferString {
8693
unsafe { BufferString::new_unchecked(buffer) }
8794
}
8895

@@ -93,9 +100,17 @@ impl BinaryViewType for StringType {
93100
fn upcast<V: BinaryViewTypeUpcast>(input: V::Input<Self>) -> V {
94101
V::from_string(input)
95102
}
103+
104+
fn empty_scalar() -> BufferString {
105+
BufferString::empty()
106+
}
107+
108+
fn matches_dtype(dtype: &DType) -> bool {
109+
matches!(dtype, DType::Utf8(_))
110+
}
96111
}
97112

98-
/// [`BinaryType`] for raw binary data.
113+
/// [`BinaryViewType`] for raw binary data.
99114
#[derive(Clone, Debug)]
100115
pub struct BinaryType;
101116
impl BinaryViewType for BinaryType {
@@ -113,7 +128,7 @@ impl BinaryViewType for BinaryType {
113128
}
114129

115130
#[inline(always)]
116-
unsafe fn scalar_from_buffer_unchecked(buffer: ByteBuffer) -> Self::Scalar {
131+
unsafe fn scalar_from_buffer_unchecked(buffer: ByteBuffer) -> ByteBuffer {
117132
buffer
118133
}
119134

@@ -124,6 +139,14 @@ impl BinaryViewType for BinaryType {
124139
fn upcast<V: BinaryViewTypeUpcast>(input: V::Input<Self>) -> V {
125140
V::from_binary(input)
126141
}
142+
143+
fn empty_scalar() -> ByteBuffer {
144+
ByteBuffer::empty()
145+
}
146+
147+
fn matches_dtype(dtype: &DType) -> bool {
148+
matches!(dtype, DType::Binary(_))
149+
}
127150
}
128151

129152
/// Trait for downcasting generic variable binary types to specific types.

vortex-vector/src/bool/scalar.rs

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,16 @@ impl BoolScalar {
2121
pub fn value(&self) -> Option<bool> {
2222
self.0
2323
}
24+
25+
/// Creates a zero (false) bool scalar.
26+
pub fn zero() -> Self {
27+
Self::new(Some(false))
28+
}
29+
30+
/// Creates a null bool scalar.
31+
pub fn null() -> Self {
32+
Self::new(None)
33+
}
2434
}
2535

2636
impl ScalarOps for BoolScalar {

vortex-vector/src/decimal/scalar.rs

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,14 @@
11
// SPDX-License-Identifier: Apache-2.0
22
// SPDX-FileCopyrightText: Copyright the Vortex contributors
33

4+
use vortex_dtype::DecimalDType;
5+
use vortex_dtype::DecimalType;
46
use vortex_dtype::DecimalTypeDowncast;
57
use vortex_dtype::DecimalTypeUpcast;
68
use vortex_dtype::NativeDecimalType;
79
use vortex_dtype::PrecisionScale;
810
use vortex_dtype::i256;
11+
use vortex_dtype::match_each_decimal_value_type;
912
use vortex_error::VortexExpect;
1013
use vortex_error::vortex_panic;
1114

@@ -56,6 +59,22 @@ impl DecimalScalar {
5659
DecimalScalar::D256(v) => v.ps.scale(),
5760
}
5861
}
62+
63+
/// Creates a zero decimal scalar of the given [`DecimalDType`].
64+
pub fn zero(decimal_dtype: &DecimalDType) -> Self {
65+
let decimal_type = DecimalType::smallest_decimal_value_type(decimal_dtype);
66+
match_each_decimal_value_type!(decimal_type, |D| {
67+
DScalar::<D>::zero(decimal_dtype).into()
68+
})
69+
}
70+
71+
/// Creates a null decimal scalar of the given [`DecimalDType`].
72+
pub fn null(decimal_dtype: &DecimalDType) -> Self {
73+
let decimal_type = DecimalType::smallest_decimal_value_type(decimal_dtype);
74+
match_each_decimal_value_type!(decimal_type, |D| {
75+
DScalar::<D>::null(decimal_dtype).into()
76+
})
77+
}
5978
}
6079

6180
impl ScalarOps for DecimalScalar {
@@ -135,6 +154,20 @@ impl<D: NativeDecimalType> DScalar<D> {
135154
pub fn value(&self) -> Option<D> {
136155
self.value
137156
}
157+
158+
/// Creates a zero decimal scalar of the given [`DecimalDType`].
159+
pub fn zero(decimal_dtype: &DecimalDType) -> Self {
160+
let ps = PrecisionScale::<D>::new(decimal_dtype.precision(), decimal_dtype.scale());
161+
// SAFETY: Zero (default) is always valid for any precision/scale.
162+
unsafe { DScalar::<D>::new_unchecked(ps, Some(D::default())) }
163+
}
164+
165+
/// Creates a null decimal scalar of the given [`DecimalDType`].
166+
pub fn null(decimal_dtype: &DecimalDType) -> Self {
167+
let ps = PrecisionScale::<D>::new(decimal_dtype.precision(), decimal_dtype.scale());
168+
// SAFETY: None is always valid for any precision/scale.
169+
unsafe { DScalar::<D>::new_unchecked(ps, None) }
170+
}
138171
}
139172

140173
impl<D: NativeDecimalType> ScalarOps for DScalar<D> {

vortex-vector/src/fixed_size_list/scalar.rs

Lines changed: 43 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,23 +1,27 @@
11
// SPDX-License-Identifier: Apache-2.0
22
// SPDX-FileCopyrightText: Copyright the Vortex contributors
33

4+
use vortex_dtype::DType;
5+
use vortex_error::vortex_panic;
6+
use vortex_mask::Mask;
7+
48
use crate::Scalar;
59
use crate::ScalarOps;
610
use crate::VectorMut;
11+
use crate::VectorMutOps;
712
use crate::VectorOps;
813
use crate::fixed_size_list::FixedSizeListVector;
9-
use vortex_mask::Mask;
1014

1115
/// A scalar value for fixed-size list types.
1216
///
13-
/// The inner value is a length-1 fsl vector.
17+
/// The inner value is a length-1 [`FixedSizeListVector`].
1418
// NOTE(ngates): the reason we don't hold Option<Vector> representing the elements is that we
1519
// wouldn't be able to go back to a vector using "repeat".
1620
#[derive(Clone, Debug)]
1721
pub struct FixedSizeListScalar(FixedSizeListVector);
1822

1923
impl FixedSizeListScalar {
20-
/// Create a new FixedSizeListScalar from a length-1 FixedSizeListVector.
24+
/// Create a new [`FixedSizeListScalar`] from a length-1 [`FixedSizeListVector`].
2125
///
2226
/// # Panics
2327
///
@@ -33,6 +37,42 @@ impl FixedSizeListScalar {
3337
}
3438
}
3539

40+
impl FixedSizeListScalar {
41+
/// Creates a zero (default elements) fixed-size list scalar of the given [`DType`].
42+
///
43+
/// # Panics
44+
///
45+
/// Panics if the dtype is not a [`DType::FixedSizeList`].
46+
pub fn zero(dtype: &DType) -> Self {
47+
if !matches!(dtype, DType::FixedSizeList(..)) {
48+
vortex_panic!("Expected FixedSizeList dtype, got {}", dtype);
49+
}
50+
51+
let mut vec = VectorMut::with_capacity(dtype, 1);
52+
vec.append_zeros(1);
53+
vec.freeze().scalar_at(0).into_fixed_size_list()
54+
}
55+
56+
/// Creates a null fixed-size list scalar of the given [`DType`].
57+
///
58+
/// # Panics
59+
///
60+
/// Panics if the dtype is not a nullable [`DType::FixedSizeList`].
61+
pub fn null(dtype: &DType) -> Self {
62+
match dtype {
63+
DType::FixedSizeList(_, _, n) if n.is_nullable() => {}
64+
DType::FixedSizeList(..) => {
65+
vortex_panic!("Expected nullable FixedSizeList dtype, got {}", dtype)
66+
}
67+
_ => vortex_panic!("Expected FixedSizeList dtype, got {}", dtype),
68+
}
69+
70+
let mut vec = VectorMut::with_capacity(dtype, 1);
71+
vec.append_nulls(1);
72+
vec.freeze().scalar_at(0).into_fixed_size_list()
73+
}
74+
}
75+
3676
impl ScalarOps for FixedSizeListScalar {
3777
fn is_valid(&self) -> bool {
3878
self.0.validity().value(0)

vortex-vector/src/listview/scalar.rs

Lines changed: 44 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,24 +1,29 @@
11
// SPDX-License-Identifier: Apache-2.0
22
// SPDX-FileCopyrightText: Copyright the Vortex contributors
33

4+
use std::sync::Arc;
5+
6+
use vortex_dtype::DType;
7+
use vortex_error::vortex_panic;
8+
use vortex_mask::Mask;
9+
use vortex_mask::MaskMut;
10+
411
use crate::Scalar;
512
use crate::ScalarOps;
613
use crate::VectorMut;
14+
use crate::VectorMutOps;
715
use crate::VectorOps;
816
use crate::listview::ListViewVector;
917
use crate::listview::ListViewVectorMut;
10-
use std::sync::Arc;
11-
use vortex_mask::Mask;
12-
use vortex_mask::MaskMut;
1318

1419
/// A scalar value for list view types.
1520
///
16-
/// The inner value is a ListViewVector with length 1.
21+
/// The inner value is a [`ListViewVector`] with length 1.
1722
#[derive(Clone, Debug)]
1823
pub struct ListViewScalar(ListViewVector);
1924

2025
impl ListViewScalar {
21-
/// Create a new ListViewScalar from a length-1 ListViewVector.
26+
/// Create a new [`ListViewScalar`] from a length-1 [`ListViewVector`].
2227
///
2328
/// # Panics
2429
///
@@ -34,6 +39,40 @@ impl ListViewScalar {
3439
}
3540
}
3641

42+
impl ListViewScalar {
43+
/// Creates a zero (empty list) list view scalar of the given [`DType`].
44+
///
45+
/// # Panics
46+
///
47+
/// Panics if the dtype is not a [`DType::List`].
48+
pub fn zero(dtype: &DType) -> Self {
49+
if !matches!(dtype, DType::List(..)) {
50+
vortex_panic!("Expected List dtype, got {}", dtype);
51+
}
52+
53+
let mut vec = VectorMut::with_capacity(dtype, 1);
54+
vec.append_zeros(1);
55+
vec.freeze().scalar_at(0).into_list()
56+
}
57+
58+
/// Creates a null list view scalar of the given [`DType`].
59+
///
60+
/// # Panics
61+
///
62+
/// Panics if the dtype is not a nullable [`DType::List`].
63+
pub fn null(dtype: &DType) -> Self {
64+
match dtype {
65+
DType::List(_, n) if n.is_nullable() => {}
66+
DType::List(..) => vortex_panic!("Expected nullable List dtype, got {}", dtype),
67+
_ => vortex_panic!("Expected List dtype, got {}", dtype),
68+
}
69+
70+
let mut vec = VectorMut::with_capacity(dtype, 1);
71+
vec.append_nulls(1);
72+
vec.freeze().scalar_at(0).into_list()
73+
}
74+
}
75+
3776
impl ScalarOps for ListViewScalar {
3877
fn is_valid(&self) -> bool {
3978
self.0.validity().value(0)

vortex-vector/src/primitive/scalar.rs

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ use vortex_dtype::PType;
88
use vortex_dtype::PTypeDowncast;
99
use vortex_dtype::PTypeUpcast;
1010
use vortex_dtype::half::f16;
11+
use vortex_dtype::match_each_native_ptype;
1112
use vortex_error::VortexExpect;
1213
use vortex_error::vortex_panic;
1314

@@ -63,6 +64,16 @@ impl PrimitiveScalar {
6364
PrimitiveScalar::F64(_) => PType::F64,
6465
}
6566
}
67+
68+
/// Creates a zero primitive scalar of the given [`PType`].
69+
pub fn zero(ptype: PType) -> Self {
70+
match_each_native_ptype!(ptype, |T| { PScalar::<T>::zero().into() })
71+
}
72+
73+
/// Creates a null primitive scalar of the given [`PType`].
74+
pub fn null(ptype: PType) -> Self {
75+
match_each_native_ptype!(ptype, |T| { PScalar::<T>::null().into() })
76+
}
6677
}
6778

6879
impl ScalarOps for PrimitiveScalar {
@@ -111,6 +122,16 @@ impl<T: NativePType> PScalar<T> {
111122
pub fn value(&self) -> Option<T> {
112123
self.0
113124
}
125+
126+
/// Creates a zero primitive scalar.
127+
pub fn zero() -> Self {
128+
Self::new(Some(T::default()))
129+
}
130+
131+
/// Creates a null primitive scalar.
132+
pub fn null() -> Self {
133+
Self::new(None)
134+
}
114135
}
115136

116137
impl<T: NativePType> From<PScalar<T>> for PrimitiveScalar {

vortex-vector/src/private.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,9 @@
77
//! Sealing these traits prevents external crates from implementing them while still allowing public
88
//! usage, which gives us the freedom to add new trait methods in the future without breaking
99
//! backward compatibility.
10+
//!
11+
//! [`VectorOps`]: crate::VectorOps
12+
//! [`VectorMutOps`]: crate::VectorMutOps
1013
1114
use vortex_dtype::NativeDecimalType;
1215
use vortex_dtype::NativePType;

0 commit comments

Comments
 (0)