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

vortex-data / vortex / 16970635821

14 Aug 2025 04:13PM UTC coverage: 85.882% (-1.8%) from 87.693%
16970635821

Pull #4215

github

web-flow
Merge 5182504a6 into f547cbca5
Pull Request #4215: Ji/vectors

80 of 1729 new or added lines in 38 files covered. (4.63%)

117 existing lines in 25 files now uncovered.

56994 of 66363 relevant lines covered (85.88%)

609331.7 hits per line

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

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

4
use std::any::Any;
5
use std::fmt::{Debug, Display, Formatter};
6
use std::hash::{Hash, Hasher};
7
use std::sync::Arc;
8

9
use dyn_hash::DynHash;
10
pub use exprs::*;
11
pub mod aliases;
12
mod analysis;
13
#[cfg(feature = "arbitrary")]
14
pub mod arbitrary;
15
pub mod dyn_traits;
16
mod encoding;
17
mod exprs;
18
mod field;
19
pub mod forms;
20
pub mod proto;
21
pub mod pruning;
22
mod registry;
23
mod scope;
24
mod scope_vars;
25
pub mod transform;
26
pub mod traversal;
27
mod vtable;
28

29
pub use analysis::*;
30
pub use between::*;
31
pub use binary::*;
32
pub use cast::*;
33
pub use encoding::*;
34
pub use get_item::*;
35
pub use is_null::*;
36
pub use like::*;
37
pub use list_contains::*;
38
pub use literal::*;
39
pub use merge::*;
40
pub use not::*;
41
pub use operators::*;
42
pub use pack::*;
43
pub use registry::*;
44
pub use root::*;
45
pub use scope::*;
46
pub use select::*;
47
use vortex_array::{Array, ArrayRef, SerializeMetadata};
48
use vortex_dtype::{DType, FieldName, FieldPath};
49
use vortex_error::{VortexExpect, VortexResult, VortexUnwrap, vortex_bail};
50
use vortex_utils::aliases::hash_set::HashSet;
51
pub use vtable::*;
52

53
use crate::dyn_traits::DynEq;
54
use crate::traversal::{NodeExt, ReferenceCollector};
55

56
pub trait IntoExpr {
57
    /// Convert this type into an expression reference.
58
    fn into_expr(self) -> ExprRef;
59
}
60

61
pub type ExprRef = Arc<dyn VortexExpr>;
62

63
/// Represents logical operation on [`ArrayRef`]s
64
pub trait VortexExpr:
65
    'static + Send + Sync + Debug + Display + DynEq + DynHash + private::Sealed + AnalysisExpr
66
{
67
    /// Convert expression reference to reference of [`Any`] type
68
    fn as_any(&self) -> &dyn Any;
69

70
    /// Convert the expression to an [`ExprRef`].
71
    fn to_expr(&self) -> ExprRef;
72

73
    /// Return the encoding of the expression.
74
    fn encoding(&self) -> ExprEncodingRef;
75

76
    /// Serialize the metadata of this expression into a bytes vector.
77
    ///
78
    /// Returns `None` if the expression does not support serialization.
79
    fn metadata(&self) -> Option<Vec<u8>> {
80
        None
81
    }
×
82

83
    /// Compute result of expression on given batch producing a new batch
84
    ///
85
    /// "Unchecked" means that this function lacks a debug assertion that the returned array matches
86
    /// the [VortexExpr::return_dtype] method. Use instead the
87
    /// [`VortexExpr::evaluate`](./trait.VortexExpr.html#method.evaluate).
88
    /// function which includes such an assertion.
89
    fn unchecked_evaluate(&self, ctx: &Scope) -> VortexResult<ArrayRef>;
90

91
    /// Returns the children of this expression.
92
    fn children(&self) -> Vec<&ExprRef>;
93

94
    /// Returns a new instance of this expression with the children replaced.
95
    fn with_children(self: Arc<Self>, children: Vec<ExprRef>) -> VortexResult<ExprRef>;
96

97
    /// Compute the type of the array returned by
98
    /// [`VortexExpr::evaluate`](./trait.VortexExpr.html#method.evaluate).
99
    fn return_dtype(&self, scope: &DType) -> VortexResult<DType>;
100
}
101

102
dyn_hash::hash_trait_object!(VortexExpr);
103

104
impl PartialEq for dyn VortexExpr {
105
    fn eq(&self, other: &Self) -> bool {
175,195✔
106
        self.dyn_eq(other.as_any())
175,195✔
107
    }
175,195✔
108
}
109

110
impl Eq for dyn VortexExpr {}
111

112
impl dyn VortexExpr + '_ {
62✔
113
    pub fn id(&self) -> ExprId {
179✔
114
        self.encoding().id()
179✔
115
    }
117✔
116

117
    pub fn is<V: VTable>(&self) -> bool {
79,381✔
118
        self.as_opt::<V>().is_some()
79,381✔
119
    }
79,381✔
120

UNCOV
121
    pub fn as_<V: VTable>(&self) -> &V::Expr {
×
UNCOV
122
        self.as_opt::<V>()
×
123
            .vortex_expect("Expr is not of the expected type")
124
    }
24✔
125

24✔
126
    pub fn as_opt<V: VTable>(&self) -> Option<&V::Expr> {
880,561✔
127
        VortexExpr::as_any(self)
880,537✔
128
            .downcast_ref::<ExprAdapter<V>>()
880,537✔
129
            .map(|e| &e.0)
880,537✔
130
    }
880,537✔
131

132
    /// Compute result of expression on given batch producing a new batch
133
    pub fn evaluate(&self, scope: &Scope) -> VortexResult<ArrayRef> {
15,520✔
134
        let result = self.unchecked_evaluate(scope)?;
15,520✔
135
        assert_eq!(
15,519✔
136
            result.dtype(),
15,519✔
137
            &self.return_dtype(scope.dtype())?,
15,519✔
138
            "Expression {} returned dtype {} but declared return_dtype of {}",
139
            self,
140
            result.dtype(),
6✔
141
            self.return_dtype(scope.dtype())?,
6✔
142
        );
6✔
143
        Ok(result)
15,233✔
144
    }
15,234✔
145
}
146

147
pub trait VortexExprExt {
148
    /// Accumulate all field references from this expression and its children in a set
149
    fn field_references(&self) -> HashSet<FieldName>;
150
}
6✔
151

6✔
152
impl VortexExprExt for ExprRef {
153
    fn field_references(&self) -> HashSet<FieldName> {
154
        let mut collector = ReferenceCollector::new();
155
        // The collector is infallible, so we can unwrap the result
156
        self.accept(&mut collector).vortex_unwrap();
157
        collector.into_fields()
158
    }
159
}
160

161
#[derive(Clone)]
162
#[repr(transparent)]
163
pub struct ExprAdapter<V: VTable>(V::Expr);
164

165
impl<V: VTable> VortexExpr for ExprAdapter<V> {
166
    fn as_any(&self) -> &dyn Any {
1,055,550✔
167
        self
1,055,550✔
168
    }
1,055,550✔
169

170
    fn to_expr(&self) -> ExprRef {
16,194✔
171
        Arc::new(ExprAdapter::<V>(self.0.clone()))
16,194✔
172
    }
16,194✔
173

354✔
174
    fn encoding(&self) -> ExprEncodingRef {
471✔
175
        V::encoding(&self.0)
471✔
176
    }
117✔
177

6✔
178
    fn metadata(&self) -> Option<Vec<u8>> {
123✔
179
        V::metadata(&self.0).map(|m| m.serialize())
123✔
180
    }
117✔
181

182
    fn unchecked_evaluate(&self, ctx: &Scope) -> VortexResult<ArrayRef> {
73,630✔
183
        V::evaluate(&self.0, ctx)
73,630✔
184
    }
73,630✔
185

186
    fn children(&self) -> Vec<&ExprRef> {
669,871✔
187
        V::children(&self.0)
669,871✔
188
    }
669,871✔
189

22✔
190
    fn with_children(self: Arc<Self>, children: Vec<ExprRef>) -> VortexResult<ExprRef> {
16,216✔
191
        if self.children().len() != children.len() {
16,216✔
192
            vortex_bail!(
193
                "Expected {} children, got {}",
228✔
194
                self.children().len(),
228✔
195
                children.len()
228✔
196
            );
197
        }
16,200✔
198
        Ok(V::with_children(&self.0, children)?.to_expr())
16,200✔
199
    }
16,194✔
200

201
    fn return_dtype(&self, scope: &DType) -> VortexResult<DType> {
124,364✔
202
        V::return_dtype(&self.0, scope)
124,364✔
203
    }
124,364✔
204
}
6✔
205

6✔
206
impl<V: VTable> Debug for ExprAdapter<V> {
6✔
207
    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
208
        Debug::fmt(&self.0, f)
40✔
209
    }
40✔
210
}
40✔
211

212
impl<V: VTable> Display for ExprAdapter<V> {
213
    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
102✔
214
        Display::fmt(&self.0, f)
102✔
215
    }
102✔
216
}
217

218
impl<V: VTable> PartialEq for ExprAdapter<V> {
219
    fn eq(&self, other: &Self) -> bool {
152,479✔
220
        PartialEq::eq(&self.0, &other.0)
152,479✔
221
    }
152,479✔
222
}
223

224
impl<V: VTable> Eq for ExprAdapter<V> {}
225

226
impl<V: VTable> Hash for ExprAdapter<V> {
227
    fn hash<H: Hasher>(&self, state: &mut H) {
262,673✔
228
        Hash::hash(&self.0, state);
262,673✔
229
    }
262,673✔
230
}
231

232
impl<V: VTable> AnalysisExpr for ExprAdapter<V> {
233
    fn stat_falsification(&self, catalog: &mut dyn StatsCatalog) -> Option<ExprRef> {
2,068✔
234
        <V::Expr as AnalysisExpr>::stat_falsification(&self.0, catalog)
2,068✔
235
    }
2,068✔
236

237
    fn max(&self, catalog: &mut dyn StatsCatalog) -> Option<ExprRef> {
1,613✔
238
        <V::Expr as AnalysisExpr>::max(&self.0, catalog)
1,613✔
239
    }
1,613✔
240

241
    fn min(&self, catalog: &mut dyn StatsCatalog) -> Option<ExprRef> {
1,680✔
242
        <V::Expr as AnalysisExpr>::min(&self.0, catalog)
1,680✔
243
    }
1,680✔
244

245
    fn nan_count(&self, catalog: &mut dyn StatsCatalog) -> Option<ExprRef> {
1,984✔
246
        <V::Expr as AnalysisExpr>::nan_count(&self.0, catalog)
1,984✔
247
    }
1,984✔
248

249
    fn field_path(&self) -> Option<FieldPath> {
1,337✔
250
        <V::Expr as AnalysisExpr>::field_path(&self.0)
1,337✔
251
    }
1,337✔
252
}
253

254
mod private {
255
    use super::*;
256

257
    pub trait Sealed {}
258

259
    impl<V: VTable> Sealed for ExprAdapter<V> {}
260
}
261

262
/// Splits top level and operations into separate expressions
263
pub fn split_conjunction(expr: &ExprRef) -> Vec<ExprRef> {
2✔
264
    let mut conjunctions = vec![];
2✔
265
    split_inner(expr, &mut conjunctions);
2✔
266
    conjunctions
2✔
267
}
2✔
268

269
fn split_inner(expr: &ExprRef, exprs: &mut Vec<ExprRef>) {
4✔
270
    match expr.as_opt::<BinaryVTable>() {
4✔
271
        Some(bexp) if bexp.op() == Operator::And => {
2✔
272
            split_inner(bexp.lhs(), exprs);
1✔
273
            split_inner(bexp.rhs(), exprs);
1✔
274
        }
1✔
275
        Some(_) | None => {
3✔
276
            exprs.push(expr.clone());
3✔
277
        }
3✔
278
    }
279
}
4✔
280

281
/// An expression wrapper that performs pointer equality.
282
#[derive(Clone)]
283
pub struct ExactExpr(pub ExprRef);
284

285
impl PartialEq for ExactExpr {
286
    fn eq(&self, other: &Self) -> bool {
11,721✔
287
        Arc::ptr_eq(&self.0, &other.0)
11,721✔
288
    }
11,721✔
289
}
290

291
impl Eq for ExactExpr {}
292

293
impl Hash for ExactExpr {
294
    fn hash<H: Hasher>(&self, state: &mut H) {
17,348✔
295
        Arc::as_ptr(&self.0).hash(state)
17,348✔
296
    }
17,348✔
297
}
298

299
#[cfg(feature = "test-harness")]
300
pub mod test_harness {
301

302
    use vortex_dtype::{DType, Nullability, PType, StructFields};
303

304
    pub fn struct_dtype() -> DType {
6✔
305
        DType::Struct(
6✔
306
            StructFields::new(
6✔
307
                ["a", "col1", "col2", "bool1", "bool2"].into(),
6✔
308
                vec![
10✔
309
                    DType::Primitive(PType::I32, Nullability::NonNullable),
10✔
310
                    DType::Primitive(PType::U16, Nullability::Nullable),
10✔
311
                    DType::Primitive(PType::U16, Nullability::Nullable),
6✔
312
                    DType::Bool(Nullability::NonNullable),
6✔
313
                    DType::Bool(Nullability::NonNullable),
6✔
314
                ],
6✔
315
            ),
6✔
316
            Nullability::NonNullable,
6✔
317
        )
6✔
318
    }
6✔
319
}
320

321
#[cfg(test)]
322
mod tests {
323
    use vortex_dtype::{DType, FieldNames, Nullability, PType, StructFields};
324
    use vortex_scalar::Scalar;
325

326
    use super::*;
327

328
    #[test]
329
    fn basic_expr_split_test() {
1✔
330
        let lhs = get_item("col1", root());
1✔
331
        let rhs = lit(1);
1✔
332
        let expr = eq(lhs, rhs);
1✔
333
        let conjunction = split_conjunction(&expr);
1✔
334
        assert_eq!(conjunction.len(), 1);
1✔
335
    }
1✔
336

337
    #[test]
338
    fn basic_conjunction_split_test() {
1✔
339
        let lhs = get_item("col1", root());
1✔
340
        let rhs = lit(1);
1✔
341
        let expr = and(lhs, rhs);
1✔
342
        let conjunction = split_conjunction(&expr);
1✔
343
        assert_eq!(conjunction.len(), 2, "Conjunction is {conjunction:?}");
1✔
344
    }
1✔
345

346
    #[test]
347
    fn expr_display() {
1✔
348
        assert_eq!(col("a").to_string(), "$.a");
1✔
349
        assert_eq!(root().to_string(), "$");
1✔
350

351
        let col1: Arc<dyn VortexExpr> = col("col1");
1✔
352
        let col2: Arc<dyn VortexExpr> = col("col2");
1✔
353
        assert_eq!(
1✔
354
            and(col1.clone(), col2.clone()).to_string(),
1✔
355
            "($.col1 and $.col2)"
356
        );
357
        assert_eq!(
1✔
358
            or(col1.clone(), col2.clone()).to_string(),
1✔
359
            "($.col1 or $.col2)"
360
        );
361
        assert_eq!(
1✔
362
            eq(col1.clone(), col2.clone()).to_string(),
1✔
363
            "($.col1 = $.col2)"
364
        );
365
        assert_eq!(
1✔
366
            not_eq(col1.clone(), col2.clone()).to_string(),
1✔
367
            "($.col1 != $.col2)"
368
        );
369
        assert_eq!(
1✔
370
            gt(col1.clone(), col2.clone()).to_string(),
1✔
371
            "($.col1 > $.col2)"
372
        );
373
        assert_eq!(
1✔
374
            gt_eq(col1.clone(), col2.clone()).to_string(),
1✔
375
            "($.col1 >= $.col2)"
376
        );
377
        assert_eq!(
1✔
378
            lt(col1.clone(), col2.clone()).to_string(),
1✔
379
            "($.col1 < $.col2)"
380
        );
381
        assert_eq!(
1✔
382
            lt_eq(col1.clone(), col2.clone()).to_string(),
1✔
383
            "($.col1 <= $.col2)"
384
        );
385

386
        assert_eq!(
1✔
387
            or(
1✔
388
                lt(col1.clone(), col2.clone()),
1✔
389
                not_eq(col1.clone(), col2.clone()),
1✔
390
            )
1✔
391
            .to_string(),
1✔
392
            "(($.col1 < $.col2) or ($.col1 != $.col2))"
393
        );
394

395
        assert_eq!(not(col1.clone()).to_string(), "(!$.col1)");
1✔
396

397
        assert_eq!(
1✔
398
            select(vec![FieldName::from("col1")], root()).to_string(),
1✔
399
            "${col1}"
400
        );
401
        assert_eq!(
1✔
402
            select(
1✔
403
                vec![FieldName::from("col1"), FieldName::from("col2")],
1✔
404
                root()
1✔
405
            )
1✔
406
            .to_string(),
1✔
407
            "${col1, col2}"
408
        );
409
        assert_eq!(
1✔
410
            select_exclude(
1✔
411
                vec![FieldName::from("col1"), FieldName::from("col2")],
1✔
412
                root()
1✔
413
            )
1✔
414
            .to_string(),
1✔
415
            "$~{col1, col2}"
416
        );
417

418
        assert_eq!(lit(Scalar::from(0u8)).to_string(), "0u8");
1✔
419
        assert_eq!(lit(Scalar::from(0.0f32)).to_string(), "0f32");
1✔
420
        assert_eq!(
1✔
421
            lit(Scalar::from(i64::MAX)).to_string(),
1✔
422
            "9223372036854775807i64"
423
        );
424
        assert_eq!(lit(Scalar::from(true)).to_string(), "true");
1✔
425
        assert_eq!(
1✔
426
            lit(Scalar::null(DType::Bool(Nullability::Nullable))).to_string(),
1✔
427
            "null"
428
        );
429

430
        assert_eq!(
1✔
431
            lit(Scalar::struct_(
1✔
432
                DType::Struct(
1✔
433
                    StructFields::new(
1✔
434
                        FieldNames::from(["dog", "cat"]),
1✔
435
                        vec![
1✔
436
                            DType::Primitive(PType::U32, Nullability::NonNullable),
1✔
437
                            DType::Utf8(Nullability::NonNullable)
1✔
438
                        ],
1✔
439
                    ),
1✔
440
                    Nullability::NonNullable
1✔
441
                ),
1✔
442
                vec![Scalar::from(32_u32), Scalar::from("rufus".to_string())]
1✔
443
            ))
1✔
444
            .to_string(),
1✔
445
            "{dog: 32u32, cat: \"rufus\"}"
446
        );
447
    }
1✔
448
}
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