• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

vortex-data / vortex / 16139349253

08 Jul 2025 09:26AM UTC coverage: 78.057% (-0.2%) from 78.253%
16139349253

push

github

web-flow
VortexExpr VTables (#3713)

Adds the same vtable machinery as arrays and layouts already use. It
uses the "Encoding" naming scheme from arrays and layouts. I don't
particularly like it, but it's consistent. Open to renames later.

Further, adds an expression registry to the Vortex session that will be
used for deserialization.

Expressions only decide their "options" serialization. So in theory, can
support many container formats, not just proto, provided each expression
can deserialize their own options format.

---------

Signed-off-by: Nicholas Gates <nick@nickgates.com>

800 of 1190 new or added lines in 38 files covered. (67.23%)

40 existing lines in 13 files now uncovered.

44100 of 56497 relevant lines covered (78.06%)

54989.55 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

54.08
/vortex-expr/src/exprs/cast.rs
1
// SPDX-License-Identifier: Apache-2.0
2
// SPDX-FileCopyrightText: Copyright the Vortex contributors
3

4
use std::fmt::Display;
5

6
use vortex_array::compute::cast as compute_cast;
7
use vortex_array::{ArrayRef, DeserializeMetadata, ProstMetadata};
8
use vortex_dtype::DType;
9
use vortex_error::{VortexResult, vortex_bail, vortex_err};
10
use vortex_proto::expr as pb;
11

12
use crate::{
13
    AnalysisExpr, ExprEncodingRef, ExprId, ExprRef, IntoExpr, Scope, ScopeDType, VTable, vtable,
14
};
15

16
vtable!(Cast);
17

18
#[allow(clippy::derived_hash_with_manual_eq)]
19
#[derive(Debug, Clone, Hash)]
20
pub struct CastExpr {
21
    target: DType,
22
    child: ExprRef,
23
}
24

25
impl PartialEq for CastExpr {
UNCOV
26
    fn eq(&self, other: &Self) -> bool {
×
NEW
27
        self.target == other.target && self.child.eq(&other.child)
×
28
    }
×
29
}
30

31
pub struct CastExprEncoding;
32

33
impl VTable for CastVTable {
34
    type Expr = CastExpr;
35
    type Encoding = CastExprEncoding;
36
    type Metadata = ProstMetadata<pb::CastOpts>;
37

NEW
38
    fn id(_encoding: &Self::Encoding) -> ExprId {
×
NEW
39
        ExprId::new_ref("cast")
×
NEW
40
    }
×
41

NEW
42
    fn encoding(_expr: &Self::Expr) -> ExprEncodingRef {
×
NEW
43
        ExprEncodingRef::new_ref(CastExprEncoding.as_ref())
×
UNCOV
44
    }
×
45

NEW
46
    fn metadata(expr: &Self::Expr) -> Option<Self::Metadata> {
×
NEW
47
        Some(ProstMetadata(pb::CastOpts {
×
NEW
48
            target: Some((&expr.target).into()),
×
NEW
49
        }))
×
UNCOV
50
    }
×
51

NEW
52
    fn children(expr: &Self::Expr) -> Vec<&ExprRef> {
×
NEW
53
        vec![&expr.child]
×
NEW
54
    }
×
55

56
    fn with_children(expr: &Self::Expr, children: Vec<ExprRef>) -> VortexResult<Self::Expr> {
1✔
57
        if children.len() != 1 {
1✔
NEW
58
            vortex_bail!(
×
NEW
59
                "Cast expression must have exactly 1 child, got {}",
×
NEW
60
                children.len()
×
NEW
61
            );
×
62
        }
1✔
63
        Ok(CastExpr {
1✔
64
            target: expr.target.clone(),
1✔
65
            child: children[0].clone(),
1✔
66
        })
1✔
67
    }
1✔
68

NEW
69
    fn build(
×
NEW
70
        _encoding: &Self::Encoding,
×
NEW
71
        metadata: &<Self::Metadata as DeserializeMetadata>::Output,
×
NEW
72
        children: Vec<ExprRef>,
×
NEW
73
    ) -> VortexResult<Self::Expr> {
×
NEW
74
        if children.len() != 1 {
×
NEW
75
            vortex_bail!(
×
NEW
76
                "Cast expression must have exactly 1 child, got {}",
×
NEW
77
                children.len()
×
NEW
78
            );
×
NEW
79
        }
×
NEW
80
        let target: DType = metadata
×
NEW
81
            .target
×
NEW
82
            .as_ref()
×
NEW
83
            .ok_or_else(|| vortex_err!("missing target dtype in CastOpts"))?
×
NEW
84
            .try_into()?;
×
NEW
85
        Ok(CastExpr {
×
NEW
86
            target,
×
NEW
87
            child: children[0].clone(),
×
NEW
88
        })
×
UNCOV
89
    }
×
90

91
    fn evaluate(expr: &Self::Expr, scope: &Scope) -> VortexResult<ArrayRef> {
1✔
92
        let array = expr.child.evaluate(scope)?;
1✔
93
        compute_cast(&array, &expr.target)
1✔
94
    }
1✔
95

96
    fn return_dtype(expr: &Self::Expr, _scope: &ScopeDType) -> VortexResult<DType> {
2✔
97
        Ok(expr.target.clone())
2✔
98
    }
2✔
99
}
100

101
impl CastExpr {
102
    pub fn new(child: ExprRef, target: DType) -> Self {
3✔
103
        Self { target, child }
3✔
104
    }
3✔
105
}
106

107
impl Display for CastExpr {
NEW
108
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
×
NEW
109
        write!(f, "cast({}, {})", self.child, self.target)
×
UNCOV
110
    }
×
111
}
112

113
impl AnalysisExpr for CastExpr {}
114

115
pub fn cast(child: ExprRef, target: DType) -> ExprRef {
3✔
116
    CastExpr::new(child, target).into_expr()
3✔
117
}
3✔
118

119
#[cfg(test)]
120
mod tests {
121
    use vortex_array::IntoArray;
122
    use vortex_array::arrays::StructArray;
123
    use vortex_buffer::buffer;
124
    use vortex_dtype::{DType, Nullability, PType};
125

126
    use crate::{ExprRef, Scope, ScopeDType, cast, get_item, root, test_harness};
127

128
    #[test]
129
    fn dtype() {
1✔
130
        let dtype = test_harness::struct_dtype();
1✔
131
        assert_eq!(
1✔
132
            cast(root(), DType::Bool(Nullability::NonNullable))
1✔
133
                .return_dtype(&ScopeDType::new(dtype))
1✔
134
                .unwrap(),
1✔
135
            DType::Bool(Nullability::NonNullable)
1✔
136
        );
1✔
137
    }
1✔
138

139
    #[test]
140
    fn replace_children() {
1✔
141
        let expr = cast(root(), DType::Bool(Nullability::Nullable));
1✔
142
        let _ = expr.with_children(vec![root()]);
1✔
143
    }
1✔
144

145
    #[test]
146
    fn evaluate() {
1✔
147
        let test_array = StructArray::from_fields(&[
1✔
148
            ("a", buffer![0i32, 1, 2].into_array()),
1✔
149
            ("b", buffer![4i64, 5, 6].into_array()),
1✔
150
        ])
1✔
151
        .unwrap()
1✔
152
        .into_array();
1✔
153

1✔
154
        let expr: ExprRef = cast(
1✔
155
            get_item("a", root()),
1✔
156
            DType::Primitive(PType::I64, Nullability::NonNullable),
1✔
157
        );
1✔
158
        let result = expr.evaluate(&Scope::new(test_array)).unwrap();
1✔
159

1✔
160
        assert_eq!(
1✔
161
            result.dtype(),
1✔
162
            &DType::Primitive(PType::I64, Nullability::NonNullable)
1✔
163
        );
1✔
164
    }
1✔
165
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc