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

vortex-data / vortex / 16935267080

13 Aug 2025 11:00AM UTC coverage: 24.312% (-63.3%) from 87.658%
16935267080

Pull #4226

github

web-flow
Merge 81b48c7fb into baa6ea202
Pull Request #4226: Support converting TimestampTZ to and from duckdb

0 of 2 new or added lines in 1 file covered. (0.0%)

20666 existing lines in 469 files now uncovered.

8726 of 35892 relevant lines covered (24.31%)

147.74 hits per line

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

0.0
/vortex-array/src/compute/like.rs
1
// SPDX-License-Identifier: Apache-2.0
2
// SPDX-FileCopyrightText: Copyright the Vortex contributors
3

4
use std::any::Any;
5
use std::sync::LazyLock;
6

7
use arcref::ArcRef;
8
use vortex_dtype::DType;
9
use vortex_error::{VortexError, VortexExpect, VortexResult, vortex_bail, vortex_err};
10

11
use crate::arrow::{Datum, from_arrow_array_with_len};
12
use crate::compute::{ComputeFn, ComputeFnVTable, InvocationArgs, Kernel, Options, Output};
13
use crate::vtable::VTable;
14
use crate::{Array, ArrayRef};
15

UNCOV
16
static LIKE_FN: LazyLock<ComputeFn> = LazyLock::new(|| {
×
UNCOV
17
    let compute = ComputeFn::new("like".into(), ArcRef::new_ref(&Like));
×
UNCOV
18
    for kernel in inventory::iter::<LikeKernelRef> {
×
UNCOV
19
        compute.register_kernel(kernel.0.clone());
×
UNCOV
20
    }
×
UNCOV
21
    compute
×
UNCOV
22
});
×
23

24
/// Perform SQL left LIKE right
25
///
26
/// There are two wildcards supported with the LIKE operator:
27
/// - %: matches zero or more characters
28
/// - _: matches exactly one character
UNCOV
29
pub fn like(
×
UNCOV
30
    array: &dyn Array,
×
UNCOV
31
    pattern: &dyn Array,
×
UNCOV
32
    options: LikeOptions,
×
UNCOV
33
) -> VortexResult<ArrayRef> {
×
UNCOV
34
    LIKE_FN
×
UNCOV
35
        .invoke(&InvocationArgs {
×
UNCOV
36
            inputs: &[array.into(), pattern.into()],
×
UNCOV
37
            options: &options,
×
UNCOV
38
        })?
×
UNCOV
39
        .unwrap_array()
×
UNCOV
40
}
×
41

42
pub struct LikeKernelRef(ArcRef<dyn Kernel>);
43
inventory::collect!(LikeKernelRef);
44

45
pub trait LikeKernel: VTable {
46
    fn like(
47
        &self,
48
        array: &Self::Array,
49
        pattern: &dyn Array,
50
        options: LikeOptions,
51
    ) -> VortexResult<Option<ArrayRef>>;
52
}
53

54
#[derive(Debug)]
55
pub struct LikeKernelAdapter<V: VTable>(pub V);
56

57
impl<V: VTable + LikeKernel> LikeKernelAdapter<V> {
58
    pub const fn lift(&'static self) -> LikeKernelRef {
×
59
        LikeKernelRef(ArcRef::new_ref(self))
×
60
    }
×
61
}
62

63
impl<V: VTable + LikeKernel> Kernel for LikeKernelAdapter<V> {
UNCOV
64
    fn invoke(&self, args: &InvocationArgs) -> VortexResult<Option<Output>> {
×
UNCOV
65
        let inputs = LikeArgs::try_from(args)?;
×
UNCOV
66
        let Some(array) = inputs.array.as_opt::<V>() else {
×
UNCOV
67
            return Ok(None);
×
68
        };
69
        Ok(V::like(&self.0, array, inputs.pattern, inputs.options)?.map(|array| array.into()))
×
UNCOV
70
    }
×
71
}
72

73
struct Like;
74

75
impl ComputeFnVTable for Like {
UNCOV
76
    fn invoke(
×
UNCOV
77
        &self,
×
UNCOV
78
        args: &InvocationArgs,
×
UNCOV
79
        kernels: &[ArcRef<dyn Kernel>],
×
UNCOV
80
    ) -> VortexResult<Output> {
×
81
        let LikeArgs {
UNCOV
82
            array,
×
UNCOV
83
            pattern,
×
UNCOV
84
            options,
×
UNCOV
85
        } = LikeArgs::try_from(args)?;
×
86

UNCOV
87
        for kernel in kernels {
×
UNCOV
88
            if let Some(output) = kernel.invoke(args)? {
×
89
                return Ok(output);
×
UNCOV
90
            }
×
91
        }
UNCOV
92
        if let Some(output) = array.invoke(&LIKE_FN, args)? {
×
93
            return Ok(output);
×
UNCOV
94
        }
×
95

96
        // Otherwise, we fall back to the Arrow implementation
UNCOV
97
        Ok(arrow_like(array, pattern, options)?.into())
×
UNCOV
98
    }
×
99

UNCOV
100
    fn return_dtype(&self, args: &InvocationArgs) -> VortexResult<DType> {
×
UNCOV
101
        let LikeArgs { array, pattern, .. } = LikeArgs::try_from(args)?;
×
UNCOV
102
        if !matches!(array.dtype(), DType::Utf8(..)) {
×
103
            vortex_bail!("Expected utf8 array, got {}", array.dtype());
×
UNCOV
104
        }
×
UNCOV
105
        if !matches!(pattern.dtype(), DType::Utf8(..)) {
×
106
            vortex_bail!("Expected utf8 pattern, got {}", array.dtype());
×
UNCOV
107
        }
×
UNCOV
108
        let nullability = array.dtype().is_nullable() || pattern.dtype().is_nullable();
×
UNCOV
109
        Ok(DType::Bool(nullability.into()))
×
UNCOV
110
    }
×
111

UNCOV
112
    fn return_len(&self, args: &InvocationArgs) -> VortexResult<usize> {
×
UNCOV
113
        let LikeArgs { array, pattern, .. } = LikeArgs::try_from(args)?;
×
UNCOV
114
        if array.len() != pattern.len() {
×
115
            vortex_bail!(
×
116
                "Length mismatch lhs len {} ({}) != rhs len {} ({})",
×
117
                array.len(),
×
118
                array.encoding_id(),
×
119
                pattern.len(),
×
120
                pattern.encoding_id()
×
121
            );
UNCOV
122
        }
×
UNCOV
123
        Ok(array.len())
×
UNCOV
124
    }
×
125

UNCOV
126
    fn is_elementwise(&self) -> bool {
×
UNCOV
127
        true
×
UNCOV
128
    }
×
129
}
130

131
/// Options for SQL LIKE function
132
#[derive(Default, Debug, Clone, Copy)]
133
pub struct LikeOptions {
134
    pub negated: bool,
135
    pub case_insensitive: bool,
136
}
137

138
impl Options for LikeOptions {
UNCOV
139
    fn as_any(&self) -> &dyn Any {
×
UNCOV
140
        self
×
UNCOV
141
    }
×
142
}
143

144
struct LikeArgs<'a> {
145
    array: &'a dyn Array,
146
    pattern: &'a dyn Array,
147
    options: LikeOptions,
148
}
149

150
impl<'a> TryFrom<&InvocationArgs<'a>> for LikeArgs<'a> {
151
    type Error = VortexError;
152

UNCOV
153
    fn try_from(value: &InvocationArgs<'a>) -> Result<Self, Self::Error> {
×
UNCOV
154
        if value.inputs.len() != 2 {
×
155
            vortex_bail!("Expected 2 inputs, found {}", value.inputs.len());
×
UNCOV
156
        }
×
UNCOV
157
        let array = value.inputs[0]
×
UNCOV
158
            .array()
×
UNCOV
159
            .ok_or_else(|| vortex_err!("Expected first input to be an array"))?;
×
UNCOV
160
        let pattern = value.inputs[1]
×
UNCOV
161
            .array()
×
UNCOV
162
            .ok_or_else(|| vortex_err!("Expected second input to be an array"))?;
×
UNCOV
163
        let options = *value
×
UNCOV
164
            .options
×
UNCOV
165
            .as_any()
×
UNCOV
166
            .downcast_ref::<LikeOptions>()
×
UNCOV
167
            .vortex_expect("Expected options to be LikeOptions");
×
168

UNCOV
169
        Ok(LikeArgs {
×
UNCOV
170
            array,
×
UNCOV
171
            pattern,
×
UNCOV
172
            options,
×
UNCOV
173
        })
×
UNCOV
174
    }
×
175
}
176

177
/// Implementation of `LikeFn` using the Arrow crate.
UNCOV
178
pub(crate) fn arrow_like(
×
UNCOV
179
    array: &dyn Array,
×
UNCOV
180
    pattern: &dyn Array,
×
UNCOV
181
    options: LikeOptions,
×
UNCOV
182
) -> VortexResult<ArrayRef> {
×
UNCOV
183
    let nullable = array.dtype().is_nullable() | pattern.dtype().is_nullable();
×
UNCOV
184
    let len = array.len();
×
UNCOV
185
    assert_eq!(
×
UNCOV
186
        array.len(),
×
UNCOV
187
        pattern.len(),
×
188
        "Arrow Like: length mismatch for {}",
×
189
        array.encoding_id()
×
190
    );
UNCOV
191
    let lhs = Datum::try_new(array)?;
×
UNCOV
192
    let rhs = Datum::try_new(pattern)?;
×
193

UNCOV
194
    let result = match (options.negated, options.case_insensitive) {
×
UNCOV
195
        (false, false) => arrow_string::like::like(&lhs, &rhs)?,
×
UNCOV
196
        (true, false) => arrow_string::like::nlike(&lhs, &rhs)?,
×
197
        (false, true) => arrow_string::like::ilike(&lhs, &rhs)?,
×
198
        (true, true) => arrow_string::like::nilike(&lhs, &rhs)?,
×
199
    };
200

UNCOV
201
    from_arrow_array_with_len(&result, len, nullable)
×
UNCOV
202
}
×
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