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

vortex-data / vortex / 16324116701

16 Jul 2025 03:46PM UTC coverage: 80.69% (-0.9%) from 81.557%
16324116701

Pull #3881

github

web-flow
Merge c08ea4033 into ced09d9a8
Pull Request #3881: feat: build with stable rust

118 of 171 new or added lines in 27 files covered. (69.01%)

174 existing lines in 102 files now uncovered.

41861 of 51879 relevant lines covered (80.69%)

157334.1 hits per line

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

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

4
use std::sync::Arc;
5

6
use itertools::Itertools;
7
use vortex::compute::{BetweenOptions, StrictComparison};
8
use vortex::dtype::Nullability;
9
use vortex::error::{VortexError, VortexExpect, VortexResult, vortex_bail, vortex_err};
10
use vortex::expr::{
11
    BetweenExpr, BinaryExpr, ExprRef, LikeExpr, LiteralExpr, NotExpr, Operator, and_collect,
12
    get_item_scope, list_contains, lit, or_collect,
13
};
14
use vortex::scalar::Scalar;
15

16
use crate::cpp::DUCKDB_VX_EXPR_TYPE;
17
use crate::duckdb::{Expression, ExpressionClass, TableFilter, TableFilterClass};
18

19
const DUCKDB_FUNCTION_NAME_CONTAINS: &str = "contains";
20

21
pub fn try_from_table_filter(value: &TableFilter, col: &str) -> VortexResult<Option<ExprRef>> {
296✔
22
    let Some(class) = value.as_class() else {
296✔
23
        return Ok(None);
18✔
24
    };
25
    Ok(Some(match class {
278✔
26
        TableFilterClass::ConstantComparison(const_) => {
178✔
27
            let scalar: Scalar = const_.value.try_into()?;
178✔
28
            let col = get_item_scope(col);
178✔
29
            BinaryExpr::new_expr(col, const_.operator.try_into()?, lit(scalar))
178✔
30
        }
31
        TableFilterClass::ConjunctionAnd(conj_and) => {
100✔
32
            let Some(children) = conj_and
100✔
33
                .children()
100✔
34
                .map(|child| try_from_table_filter(&child, col))
186✔
35
                .try_collect::<_, Option<Vec<_>>, _>()?
100✔
36
            else {
37
                return Ok(None);
18✔
38
            };
39

40
            and_collect(children).unwrap_or_else(|| lit(true))
82✔
41
        }
42
        // This is a disjunction.
43
        TableFilterClass::ConjunctionOr(disjuction_or) => {
×
44
            let Some(children) = disjuction_or
×
45
                .children()
×
46
                .map(|child| try_from_table_filter(&child, col))
×
47
                .try_collect::<_, Option<Vec<_>>, _>()?
×
48
            else {
49
                return Ok(None);
×
50
            };
51

52
            or_collect(children).unwrap_or_else(|| lit(false))
×
53
        }
54
        _ => todo!("cannot convert table filter {:?}", value),
×
55
    }))
56
}
296✔
57

58
fn like_pattern_str(value: &Expression) -> VortexResult<Option<String>> {
3✔
59
    match value.as_class().vortex_expect("unknown class") {
3✔
60
        ExpressionClass::BoundConstant(constant) => {
3✔
61
            Ok(Some(format!("%{}%", constant.value.as_string())))
3✔
62
        }
63
        _ => Ok(None),
×
64
    }
65
}
3✔
66

67
pub fn try_from_bound_expression(value: &Expression) -> VortexResult<Option<ExprRef>> {
417✔
68
    let Some(value) = value.as_class() else {
417✔
69
        vortex_bail!("no expression class id {:?}", value.as_class_id())
×
70
    };
71
    Ok(Some(match value {
417✔
72
        ExpressionClass::BoundColumnRef(col) => get_item_scope(col.name.to_str()?),
113✔
73
        ExpressionClass::BoundConstant(const_) => lit(Scalar::try_from(const_.value)?),
133✔
74
        ExpressionClass::BoundComparison(compare) => {
70✔
75
            let operator: Operator = compare.op.try_into()?;
70✔
76

77
            let Some(left) = try_from_bound_expression(&compare.left)? else {
70✔
78
                return Ok(None);
×
79
            };
80
            let Some(right) = try_from_bound_expression(&compare.right)? else {
70✔
81
                return Ok(None);
×
82
            };
83

84
            BinaryExpr::new_expr(left, operator, right)
70✔
85
        }
86
        ExpressionClass::BoundBetween(between) => {
23✔
87
            let Some(array) = try_from_bound_expression(&between.input)? else {
23✔
88
                return Ok(None);
×
89
            };
90
            let Some(lower) = try_from_bound_expression(&between.lower)? else {
23✔
91
                return Ok(None);
×
92
            };
93
            let Some(upper) = try_from_bound_expression(&between.upper)? else {
23✔
94
                return Ok(None);
×
95
            };
96
            BetweenExpr::new_expr(
23✔
97
                array,
23✔
98
                lower,
23✔
99
                upper,
23✔
100
                BetweenOptions {
101
                    lower_strict: if between.lower_inclusive {
23✔
102
                        StrictComparison::NonStrict
22✔
103
                    } else {
104
                        StrictComparison::Strict
1✔
105
                    },
106
                    upper_strict: if between.upper_inclusive {
23✔
107
                        StrictComparison::NonStrict
6✔
108
                    } else {
109
                        StrictComparison::Strict
17✔
110
                    },
111
                },
112
            )
113
        }
114
        ExpressionClass::BoundOperator(operator) => match operator.op {
31✔
115
            DUCKDB_VX_EXPR_TYPE::DUCKDB_VX_EXPR_TYPE_OPERATOR_NOT => {
116
                let children = operator.children().collect_vec();
4✔
117
                assert_eq!(children.len(), 1);
4✔
118
                let Some(child) = try_from_bound_expression(&children[0])? else {
4✔
119
                    return Ok(None);
4✔
120
                };
121
                NotExpr::new_expr(child)
×
122
            }
123
            DUCKDB_VX_EXPR_TYPE::DUCKDB_VX_EXPR_TYPE_COMPARE_IN => {
124
                // First child is element, rest form the list.
125
                let children = operator.children().collect_vec();
27✔
126
                assert!(children.len() >= 2);
27✔
127
                let Some(element) = try_from_bound_expression(&children[0])? else {
27✔
128
                    return Ok(None);
20✔
129
                };
130

131
                let Some(list_elements) = children
7✔
132
                    .iter()
7✔
133
                    .skip(1)
7✔
134
                    .map(|c| {
27✔
135
                        let Some(value) = try_from_bound_expression(c)? else {
27✔
136
                            return Ok(None);
×
137
                        };
138
                        Ok(Some(
139
                            LiteralExpr::maybe_from(&value)
27✔
140
                                .ok_or_else(|| {
27✔
141
                                    vortex_err!("cannot have a non literal in a in_list")
×
UNCOV
142
                                })?
×
143
                                .value()
27✔
144
                                .clone(),
27✔
145
                        ))
146
                    })
27✔
147
                    .collect::<VortexResult<Option<Vec<_>>>>()?
7✔
148
                else {
149
                    return Ok(None);
×
150
                };
151
                let list = Scalar::list(
7✔
152
                    Arc::new(list_elements[0].dtype().clone()),
7✔
153
                    list_elements,
7✔
154
                    Nullability::Nullable,
7✔
155
                );
156
                list_contains(lit(list), element)
7✔
157
            }
158
            _ => todo!("operator {:?}", operator.op),
×
159
        },
160
        ExpressionClass::BoundFunction(func) => match func.scalar_function.name() {
43✔
161
            DUCKDB_FUNCTION_NAME_CONTAINS => {
43✔
162
                let children = func.children().collect_vec();
3✔
163
                assert_eq!(children.len(), 2);
3✔
164
                let Some(value) = try_from_bound_expression(&children[0])? else {
3✔
165
                    return Ok(None);
×
166
                };
167
                let Some(pattern_lit) = like_pattern_str(&children[1])? else {
3✔
168
                    vortex_bail!("expected pattern to be bound string")
×
169
                };
170
                let pattern = LiteralExpr::new_expr(pattern_lit);
3✔
171
                LikeExpr::new_expr(value, pattern, false, false)
3✔
172
            }
173
            _ => {
174
                log::debug!("bound function {}", func.scalar_function.name());
40✔
175
                return Ok(None);
40✔
176
            }
177
        },
178
        ExpressionClass::BoundConjunction(conj) => {
4✔
179
            let Some(children) = conj
4✔
180
                .children()
4✔
181
                .map(|c| try_from_bound_expression(&c))
8✔
182
                .collect::<VortexResult<Option<Vec<_>>>>()?
4✔
183
            else {
184
                return Ok(None);
×
185
            };
186
            match conj.op {
4✔
187
                DUCKDB_VX_EXPR_TYPE::DUCKDB_VX_EXPR_TYPE_CONJUNCTION_AND => {
188
                    and_collect(children).vortex_expect("cannot be empty")
×
189
                }
190
                DUCKDB_VX_EXPR_TYPE::DUCKDB_VX_EXPR_TYPE_CONJUNCTION_OR => {
191
                    or_collect(children).vortex_expect("cannot be empty")
4✔
192
                }
193
                _ => vortex_bail!("unexpected operator {:?} in bound conjunction", conj.op),
×
194
            }
195
        }
196
    }))
197
}
417✔
198

199
impl TryFrom<DUCKDB_VX_EXPR_TYPE> for Operator {
200
    type Error = VortexError;
201

202
    fn try_from(value: DUCKDB_VX_EXPR_TYPE) -> VortexResult<Self> {
248✔
203
        Ok(match value {
248✔
204
            DUCKDB_VX_EXPR_TYPE::DUCKDB_VX_EXPR_TYPE_INVALID => vortex_bail!("invalid expr"),
×
205
            DUCKDB_VX_EXPR_TYPE::DUCKDB_VX_EXPR_TYPE_COMPARE_EQUAL => Operator::Eq,
56✔
206
            DUCKDB_VX_EXPR_TYPE::CDUCKDB_VX_EXPR_TYPE_OMPARE_NOTEQUAL => Operator::NotEq,
2✔
207
            DUCKDB_VX_EXPR_TYPE::DUCKDB_VX_EXPR_TYPE_COMPARE_LESSTHAN => Operator::Lt,
16✔
208
            DUCKDB_VX_EXPR_TYPE::DUCKDB_VX_EXPR_TYPE_COMPARE_GREATERTHAN => Operator::Gt,
9✔
209
            DUCKDB_VX_EXPR_TYPE::DUCKDB_VX_EXPR_TYPE_COMPARE_LESSTHANOREQUALTO => Operator::Lte,
82✔
210
            DUCKDB_VX_EXPR_TYPE::DUCKDB_VX_EXPR_TYPE_COMPARE_GREATERTHANOREQUALTO => Operator::Gte,
83✔
211
            _ => todo!("cannot convert {:?}", value),
×
212
        })
213
    }
248✔
214
}
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