• 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

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

4
use std::fmt::{Debug, Display};
5

6
use vortex_array::compute::{BetweenOptions, StrictComparison, between as between_compute};
7
use vortex_array::{ArrayRef, DeserializeMetadata, ProstMetadata};
8
use vortex_dtype::DType;
9
use vortex_dtype::DType::Bool;
10
use vortex_error::{VortexResult, vortex_bail};
11
use vortex_proto::expr as pb;
12

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

18
vtable!(Between);
19

20
#[allow(clippy::derived_hash_with_manual_eq)]
21
#[derive(Clone, Debug, Hash)]
22
pub struct BetweenExpr {
23
    arr: ExprRef,
24
    lower: ExprRef,
25
    upper: ExprRef,
26
    options: BetweenOptions,
27
}
28

29
impl PartialEq for BetweenExpr {
30
    fn eq(&self, other: &Self) -> bool {
175✔
31
        self.arr.eq(&other.arr)
175✔
32
            && self.lower.eq(&other.lower)
175✔
33
            && self.upper.eq(&other.upper)
175✔
34
            && self.options == other.options
175✔
35
    }
175✔
36
}
37

38
pub struct BetweenExprEncoding;
39

40
impl VTable for BetweenVTable {
41
    type Expr = BetweenExpr;
42
    type Encoding = BetweenExprEncoding;
43
    type Metadata = ProstMetadata<pb::BetweenOpts>;
44

45
    fn id(_encoding: &Self::Encoding) -> ExprId {
107✔
46
        ExprId::new_ref("between")
107✔
47
    }
107✔
48

49
    fn encoding(_expr: &Self::Expr) -> ExprEncodingRef {
1✔
50
        ExprEncodingRef::new_ref(BetweenExprEncoding.as_ref())
1✔
51
    }
1✔
52

53
    fn metadata(expr: &Self::Expr) -> Option<Self::Metadata> {
1✔
54
        Some(ProstMetadata(pb::BetweenOpts {
1✔
55
            lower_strict: expr.options.lower_strict == StrictComparison::Strict,
1✔
56
            upper_strict: expr.options.upper_strict == StrictComparison::Strict,
1✔
57
        }))
1✔
58
    }
1✔
59

60
    fn children(expr: &Self::Expr) -> Vec<&ExprRef> {
883✔
61
        vec![&expr.arr, &expr.lower, &expr.upper]
883✔
62
    }
883✔
63

64
    fn with_children(expr: &Self::Expr, children: Vec<ExprRef>) -> VortexResult<Self::Expr> {
42✔
65
        if children.len() != 3 {
42✔
NEW
66
            vortex_bail!(
×
NEW
67
                "Between expression must have exactly 3 children, got {}",
×
NEW
68
                children.len()
×
NEW
69
            );
×
70
        }
42✔
71
        Ok(BetweenExpr::new(
42✔
72
            children[0].clone(),
42✔
73
            children[1].clone(),
42✔
74
            children[2].clone(),
42✔
75
            expr.options.clone(),
42✔
76
        ))
42✔
77
    }
42✔
78

79
    fn build(
1✔
80
        _encoding: &Self::Encoding,
1✔
81
        metadata: &<Self::Metadata as DeserializeMetadata>::Output,
1✔
82
        children: Vec<ExprRef>,
1✔
83
    ) -> VortexResult<Self::Expr> {
1✔
84
        Ok(BetweenExpr::new(
1✔
85
            children[0].clone(),
1✔
86
            children[1].clone(),
1✔
87
            children[2].clone(),
1✔
88
            BetweenOptions {
1✔
89
                lower_strict: if metadata.lower_strict {
1✔
90
                    StrictComparison::Strict
1✔
91
                } else {
NEW
92
                    StrictComparison::NonStrict
×
93
                },
94
                upper_strict: if metadata.upper_strict {
1✔
95
                    StrictComparison::Strict
1✔
96
                } else {
NEW
97
                    StrictComparison::NonStrict
×
98
                },
99
            },
100
        ))
101
    }
1✔
102

103
    fn evaluate(expr: &Self::Expr, scope: &Scope) -> VortexResult<ArrayRef> {
21✔
104
        let arr_val = expr.arr.unchecked_evaluate(scope)?;
21✔
105
        let lower_arr_val = expr.lower.unchecked_evaluate(scope)?;
21✔
106
        let upper_arr_val = expr.upper.unchecked_evaluate(scope)?;
21✔
107

108
        between_compute(&arr_val, &lower_arr_val, &upper_arr_val, &expr.options)
21✔
109
    }
21✔
110

111
    fn return_dtype(expr: &Self::Expr, scope: &ScopeDType) -> VortexResult<DType> {
42✔
112
        let arr_dt = expr.arr.return_dtype(scope)?;
42✔
113
        let lower_dt = expr.lower.return_dtype(scope)?;
42✔
114
        let upper_dt = expr.upper.return_dtype(scope)?;
42✔
115

116
        if !arr_dt.eq_ignore_nullability(&lower_dt) {
42✔
NEW
117
            vortex_bail!(
×
NEW
118
                "Array dtype {} does not match lower dtype {}",
×
NEW
119
                arr_dt,
×
NEW
120
                lower_dt
×
NEW
121
            );
×
122
        }
42✔
123
        if !arr_dt.eq_ignore_nullability(&upper_dt) {
42✔
NEW
124
            vortex_bail!(
×
NEW
125
                "Array dtype {} does not match upper dtype {}",
×
NEW
126
                arr_dt,
×
NEW
127
                upper_dt
×
NEW
128
            );
×
129
        }
42✔
130

42✔
131
        Ok(Bool(
42✔
132
            arr_dt.nullability() | lower_dt.nullability() | upper_dt.nullability(),
42✔
133
        ))
42✔
134
    }
42✔
135
}
136

137
impl BetweenExpr {
138
    pub fn new(arr: ExprRef, lower: ExprRef, upper: ExprRef, options: BetweenOptions) -> Self {
98✔
139
        Self {
98✔
140
            arr,
98✔
141
            lower,
98✔
142
            upper,
98✔
143
            options,
98✔
144
        }
98✔
145
    }
98✔
146

147
    pub fn new_expr(
21✔
148
        arr: ExprRef,
21✔
149
        lower: ExprRef,
21✔
150
        upper: ExprRef,
21✔
151
        options: BetweenOptions,
21✔
152
    ) -> ExprRef {
21✔
153
        Self::new(arr, lower, upper, options).into_expr()
21✔
154
    }
21✔
155

156
    pub fn to_binary_expr(&self) -> ExprRef {
×
NEW
157
        let lhs = BinaryExpr::new(
×
158
            self.lower.clone(),
×
159
            self.options.lower_strict.to_operator().into(),
×
160
            self.arr.clone(),
×
161
        );
×
NEW
162
        let rhs = BinaryExpr::new(
×
163
            self.arr.clone(),
×
164
            self.options.upper_strict.to_operator().into(),
×
165
            self.upper.clone(),
×
166
        );
×
NEW
167
        BinaryExpr::new(lhs.into_expr(), crate::Operator::And, rhs.into_expr()).into_expr()
×
168
    }
×
169
}
170

171
impl Display for BetweenExpr {
172
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
×
173
        write!(
×
174
            f,
×
175
            "({} {} {} {} {})",
×
176
            self.lower,
×
177
            self.options.lower_strict.to_operator(),
×
178
            self.arr,
×
179
            self.options.upper_strict.to_operator(),
×
180
            self.upper
×
181
        )
×
182
    }
×
183
}
184

185
impl AnalysisExpr for BetweenExpr {}
186

187
pub fn between(arr: ExprRef, lower: ExprRef, upper: ExprRef, options: BetweenOptions) -> ExprRef {
7✔
188
    BetweenExpr::new(arr, lower, upper, options).into_expr()
7✔
189
}
7✔
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