Skip to content

Commit ee25100

Browse files
committed
Vector Casting
Signed-off-by: Nicholas Gates <[email protected]>
1 parent 52ee607 commit ee25100

File tree

9 files changed

+192
-15
lines changed

9 files changed

+192
-15
lines changed
Lines changed: 27 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -4,16 +4,16 @@
44
use std::fmt::Formatter;
55
use std::ops::Deref;
66

7-
use prost::Message;
8-
use vortex_dtype::DType;
9-
use vortex_error::{VortexExpect, VortexResult, vortex_bail, vortex_err};
10-
use vortex_proto::expr as pb;
11-
12-
use crate::ArrayRef;
137
use crate::compute::cast as compute_cast;
148
use crate::expr::expression::Expression;
159
use crate::expr::{ChildName, ExprId, ExpressionView, StatsCatalog, VTable, VTableExt};
1610
use crate::stats::Stat;
11+
use crate::ArrayRef;
12+
use prost::Message;
13+
use vortex_dtype::DType;
14+
use vortex_error::{vortex_bail, vortex_err, VortexExpect, VortexResult};
15+
use vortex_proto::expr as pb;
16+
use vortex_vector::Vector;
1717

1818
/// A cast expression that converts values to a target data type.
1919
pub struct Cast;
@@ -87,6 +87,16 @@ impl VTable for Cast {
8787
})
8888
}
8989

90+
fn execute(
91+
&self,
92+
expr: &ExpressionView<Self>,
93+
vector: &Vector,
94+
dtype: &DType,
95+
) -> VortexResult<Vector> {
96+
let input = expr.child(0).execute(vector, dtype)?;
97+
vortex_compute::cast::Cast::cast(&input, dtype)
98+
}
99+
90100
fn stat_expression(
91101
&self,
92102
expr: &ExpressionView<Self>,
@@ -120,6 +130,15 @@ impl VTable for Cast {
120130
}
121131
}
122132

133+
/// A trait that defines the compute function for performing a cast.
134+
///
135+
/// The `try_cast` method attempts to convert the vector to the specified data type,
136+
/// returning a `VortexResult` that contains the casted vector or an error if the
137+
/// cast operation fails.
138+
trait VectorCast {
139+
fn try_cast(self, dtype: &DType) -> VortexResult<Vector>;
140+
}
141+
123142
/// Creates an expression that casts values to a target data type.
124143
///
125144
/// Converts the input expression's values to the specified target type.
@@ -141,11 +160,11 @@ mod tests {
141160
use vortex_error::VortexUnwrap as _;
142161

143162
use super::cast;
144-
use crate::IntoArray;
145163
use crate::arrays::StructArray;
146164
use crate::expr::exprs::get_item::get_item;
147165
use crate::expr::exprs::root::root;
148-
use crate::expr::{Expression, test_harness};
166+
use crate::expr::{test_harness, Expression};
167+
use crate::IntoArray;
149168

150169
#[test]
151170
fn dtype() {

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

Lines changed: 17 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -5,17 +5,17 @@ pub mod transform;
55

66
use std::fmt::{Display, Formatter};
77

8+
use crate::expr::expression::Expression;
9+
use crate::expr::field::DisplayFieldNames;
10+
use crate::expr::{ChildName, ExprId, ExpressionView, VTable, VTableExt};
11+
use crate::{ArrayRef, IntoArray, ToCanonical};
812
use itertools::Itertools;
913
use prost::Message;
1014
use vortex_dtype::{DType, FieldNames};
11-
use vortex_error::{VortexExpect, VortexResult, vortex_bail, vortex_err};
15+
use vortex_error::{vortex_bail, vortex_err, VortexExpect, VortexResult};
1216
use vortex_proto::expr::select_opts::Opts;
1317
use vortex_proto::expr::{FieldNames as ProtoFieldNames, SelectOpts};
14-
15-
use crate::expr::expression::Expression;
16-
use crate::expr::field::DisplayFieldNames;
17-
use crate::expr::{ChildName, ExprId, ExpressionView, VTable, VTableExt};
18-
use crate::{ArrayRef, IntoArray, ToCanonical};
18+
use vortex_vector::Vector;
1919

2020
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
2121
pub enum FieldSelection {
@@ -144,6 +144,17 @@ impl VTable for Select {
144144
}?
145145
.into_array())
146146
}
147+
148+
fn execute(
149+
&self,
150+
_expr: &ExpressionView<Self>,
151+
_vector: &Vector,
152+
_dtype: &DType,
153+
) -> VortexResult<Vector> {
154+
vortex_bail!(
155+
"Select expressions cannot be executed. They must be removed during optimization."
156+
)
157+
}
147158
}
148159

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

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

vortex-compute/src/cast/pvector.rs

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

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;

vortex-vector/src/primitive/generic.rs

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,11 +8,15 @@ use std::ops::RangeBounds;
88

99
use vortex_buffer::Buffer;
1010
use vortex_dtype::NativePType;
11-
use vortex_error::{VortexExpect, VortexResult, vortex_ensure};
11+
use vortex_error::{vortex_ensure, VortexExpect, VortexResult};
1212
use vortex_mask::Mask;
1313

1414
use crate::primitive::{PScalar, PVectorMut};
15+
<<<<<<< Updated upstream
1516
use crate::{Scalar, VectorOps};
17+
=======
18+
use crate::VectorOps;
19+
>>>>>>> Stashed changes
1620

1721
/// An immutable vector of generic primitive values.
1822
///

0 commit comments

Comments
 (0)