Skip to content

Commit b5d28a1

Browse files
authored
Vector cast (#5461)
Signed-off-by: Nicholas Gates <[email protected]>
1 parent 4584165 commit b5d28a1

File tree

8 files changed

+169
-0
lines changed

8 files changed

+169
-0
lines changed
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ use prost::Message;
88
use vortex_dtype::DType;
99
use vortex_error::{VortexExpect, VortexResult, vortex_bail, vortex_err};
1010
use vortex_proto::expr as pb;
11+
use vortex_vector::Vector;
1112

1213
use crate::ArrayRef;
1314
use crate::compute::cast as compute_cast;
@@ -87,6 +88,16 @@ impl VTable for Cast {
8788
})
8889
}
8990

91+
fn execute(
92+
&self,
93+
expr: &ExpressionView<Self>,
94+
vector: &Vector,
95+
dtype: &DType,
96+
) -> VortexResult<Vector> {
97+
let input = expr.child(0).execute(vector, dtype)?;
98+
vortex_compute::cast::Cast::cast(&input, dtype)
99+
}
100+
90101
fn stat_expression(
91102
&self,
92103
expr: &ExpressionView<Self>,

vortex-array/src/expr/exprs/select/mod.rs

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ use vortex_dtype::{DType, FieldNames};
1111
use vortex_error::{VortexExpect, VortexResult, vortex_bail, vortex_err};
1212
use vortex_proto::expr::select_opts::Opts;
1313
use vortex_proto::expr::{FieldNames as ProtoFieldNames, SelectOpts};
14+
use vortex_vector::Vector;
1415

1516
use crate::expr::expression::Expression;
1617
use crate::expr::field::DisplayFieldNames;
@@ -144,6 +145,17 @@ impl VTable for Select {
144145
}?
145146
.into_array())
146147
}
148+
149+
fn execute(
150+
&self,
151+
_expr: &ExpressionView<Self>,
152+
_vector: &Vector,
153+
_dtype: &DType,
154+
) -> VortexResult<Vector> {
155+
vortex_bail!(
156+
"Select expressions cannot be executed. They must be removed during optimization."
157+
)
158+
}
147159
}
148160

149161
/// Creates an expression that selects (includes) specific fields from an array.

vortex-buffer/src/trusted_len.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -162,5 +162,6 @@ where
162162
}
163163

164164
// Arrow bit iterators
165+
unsafe impl<'a> TrustedLen for crate::bit::BitIterator<'a> {}
165166
unsafe impl<'a> TrustedLen for crate::bit::BitChunkIterator<'a> {}
166167
unsafe impl<'a> TrustedLen for crate::bit::UnalignedBitChunkIterator<'a> {}

vortex-compute/src/cast/bool.rs

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
// SPDX-License-Identifier: Apache-2.0
2+
// SPDX-FileCopyrightText: Copyright the Vortex contributors
3+
4+
use num_traits::{One, Zero};
5+
use vortex_buffer::Buffer;
6+
use vortex_dtype::{DType, match_each_native_ptype};
7+
use vortex_error::{VortexResult, vortex_bail};
8+
use vortex_vector::bool::BoolVector;
9+
use vortex_vector::null::NullVector;
10+
use vortex_vector::primitive::PVector;
11+
use vortex_vector::{Vector, VectorOps};
12+
13+
use crate::cast::Cast;
14+
15+
impl Cast for BoolVector {
16+
fn cast(&self, dtype: &DType) -> VortexResult<Vector> {
17+
match dtype {
18+
DType::Null if self.validity().all_false() => {
19+
// Can cast an all-null BoolVector to NullVector.
20+
Ok(NullVector::new(self.len()).into())
21+
}
22+
DType::Bool(n) if n.is_nullable() || self.validity().all_true() => {
23+
// If the target dtype is nullable, or if the source BoolVector has no nulls,
24+
// we can cast directly to BoolVector.
25+
Ok(self.clone().into())
26+
}
27+
DType::Primitive(ptype, _) => {
28+
match_each_native_ptype!(ptype, |T| {
29+
Ok(PVector::<T>::new(
30+
Buffer::<T>::from_trusted_len_iter(
31+
self.bits()
32+
.iter()
33+
.map(|b| if b { T::one() } else { T::zero() }),
34+
),
35+
self.validity().clone(),
36+
)
37+
.into())
38+
})
39+
}
40+
DType::Extension(ext_dtype) => self.cast(ext_dtype.storage_dtype()),
41+
_ => {
42+
vortex_bail!("Cannot cast BoolVector to type {}", dtype);
43+
}
44+
}
45+
}
46+
}

vortex-compute/src/cast/mod.rs

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
// SPDX-License-Identifier: Apache-2.0
2+
// SPDX-FileCopyrightText: Copyright the Vortex contributors
3+
4+
//! Lossless casting of Vortex vectors for different logical data types.
5+
6+
mod bool;
7+
mod null;
8+
mod pvector;
9+
10+
use vortex_dtype::DType;
11+
use vortex_error::{VortexResult, vortex_bail};
12+
use vortex_vector::Vector;
13+
14+
/// Trait for casting vectors to different data types.
15+
pub trait Cast {
16+
/// Cast the vector to the specified data type.
17+
fn cast(&self, dtype: &DType) -> VortexResult<Vector>;
18+
}
19+
20+
impl Cast for Vector {
21+
fn cast(&self, dtype: &DType) -> VortexResult<Vector> {
22+
// Switch to macro once all vector types implement Cast
23+
// match_each_vector!(self, |v| { Cast::cast(v, dtype) })
24+
match self {
25+
Vector::Null(v) => Cast::cast(v, dtype),
26+
Vector::Bool(v) => Cast::cast(v, dtype),
27+
Vector::Primitive(v) => Cast::cast(v, dtype),
28+
_ => {
29+
vortex_bail!("Casting not implemented for vector type {:?}", self);
30+
}
31+
}
32+
}
33+
}

vortex-compute/src/cast/null.rs

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
// SPDX-License-Identifier: Apache-2.0
2+
// SPDX-FileCopyrightText: Copyright the Vortex contributors
3+
4+
use vortex_dtype::DType;
5+
use vortex_error::{VortexResult, vortex_bail};
6+
use vortex_vector::null::NullVector;
7+
use vortex_vector::{Vector, VectorMut, VectorMutOps, VectorOps};
8+
9+
use crate::cast::Cast;
10+
11+
impl Cast for NullVector {
12+
fn cast(&self, dtype: &DType) -> VortexResult<Vector> {
13+
if dtype.is_nullable() {
14+
// We can create an all-null vector of _any_ type.
15+
let mut vec = VectorMut::with_capacity(dtype, self.len());
16+
vec.append_nulls(self.len());
17+
Ok(vec.freeze())
18+
} else {
19+
vortex_bail!("Cannot cast NullVector to non-nullable type {}", dtype);
20+
}
21+
}
22+
}

vortex-compute/src/cast/pvector.rs

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
// SPDX-License-Identifier: Apache-2.0
2+
// SPDX-FileCopyrightText: Copyright the Vortex contributors
3+
4+
use vortex_dtype::{DType, NativePType};
5+
use vortex_error::{VortexResult, vortex_bail};
6+
use vortex_vector::null::NullVector;
7+
use vortex_vector::primitive::{PVector, PrimitiveVector};
8+
use vortex_vector::{Vector, VectorOps, match_each_pvector};
9+
10+
use crate::cast::Cast;
11+
12+
impl Cast for PrimitiveVector {
13+
fn cast(&self, dtype: &DType) -> VortexResult<Vector> {
14+
match_each_pvector!(self, |v| { Cast::cast(v, dtype) })
15+
}
16+
}
17+
18+
impl<T: NativePType> Cast for PVector<T> {
19+
fn cast(&self, dtype: &DType) -> VortexResult<Vector> {
20+
match dtype {
21+
// Can cast an all-null PVector to NullVector.
22+
DType::Null if self.validity().all_false() => Ok(NullVector::new(self.len()).into()),
23+
// We're already the correct PType, and we have compatible nullability.
24+
DType::Primitive(target_ptype, n)
25+
if *target_ptype == T::PTYPE && (n.is_nullable() || self.validity().all_true()) =>
26+
{
27+
Ok(self.clone().into())
28+
}
29+
// We're not the correct PType, but we do have compatible nullability.
30+
DType::Primitive(target_ptype, n) if n.is_nullable() || self.validity().all_true() => {
31+
vortex_bail!(
32+
"Casting from PType {} to PType {} not yet implemented",
33+
T::PTYPE,
34+
target_ptype
35+
);
36+
}
37+
DType::Extension(ext_dtype) => self.cast(ext_dtype.storage_dtype()),
38+
_ => {
39+
vortex_bail!("Cannot cast {:?} to dtype {}", self, dtype);
40+
}
41+
}
42+
}
43+
}

vortex-compute/src/lib.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
pub mod arithmetic;
1111
#[cfg(feature = "arrow")]
1212
pub mod arrow;
13+
pub mod cast;
1314
pub mod comparison;
1415
pub mod expand;
1516
pub mod filter;

0 commit comments

Comments
 (0)