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

vortex-data / vortex / 16509961961

24 Jul 2025 11:24PM UTC coverage: 81.792% (+0.7%) from 81.091%
16509961961

Pull #4002

github

web-flow
Merge fe709c0ab into f14d4e4bc
Pull Request #4002: fix: Implement compact for ListArray

115 of 128 new or added lines in 6 files covered. (89.84%)

234 existing lines in 12 files now uncovered.

43260 of 52890 relevant lines covered (81.79%)

170683.44 hits per line

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

76.74
/vortex-duckdb/src/duckdb/table_function/mod.rs
1
// SPDX-License-Identifier: Apache-2.0
2
// SPDX-FileCopyrightText: Copyright the Vortex contributors
3

4
use std::ffi::{CStr, CString, c_void};
5
use std::fmt::Debug;
6
use std::ptr;
7

8
use vortex::error::{VortexExpect, VortexResult};
9
mod bind;
10
mod cardinality;
11
mod init;
12
mod partition;
13
mod pushdown_complex_filter;
14

15
pub use bind::*;
16
pub use init::*;
17

18
use crate::duckdb::LogicalType;
19
use crate::duckdb::connection::Connection;
20
use crate::duckdb::data_chunk::DataChunk;
21
use crate::duckdb::expr::Expression;
22
use crate::duckdb::table_function::cardinality::cardinality_callback;
23
use crate::duckdb::table_function::partition::get_partition_data_callback;
24
use crate::duckdb::table_function::pushdown_complex_filter::pushdown_complex_filter_callback;
25
use crate::{cpp, duckdb_try};
26

27
/// A trait that defines the supported operations for a table function in DuckDB.
28
///
29
/// This trait does not yet cover the full C++ API, see table_function.hpp.
30
pub trait TableFunction: Sized + Debug {
31
    type BindData: Send + Clone;
32
    type GlobalState: Send + Sync;
33
    type LocalState;
34

35
    /// Whether the table function supports projection pushdown.
36
    /// If not supported a projection will be added that filters out unused columns.
37
    const PROJECTION_PUSHDOWN: bool = false;
38

39
    /// Whether the table function supports filter pushdown.
40
    /// If not supported a filter will be added that applies the table filter directly.
41
    const FILTER_PUSHDOWN: bool = false;
42

43
    /// Whether the table function can immediately prune out filter columns that are unused
44
    /// in the remainder of the query plan.
45
    /// e.g. "SELECT i FROM tbl WHERE j = 42;"
46
    ///   - j does not need to leave the table function at all.
47
    const FILTER_PRUNE: bool = false;
48

49
    /// Returns the parameters of the table function.
50
    fn parameters() -> Vec<LogicalType> {
×
51
        // By default, we don't have any parameters.
UNCOV
52
        vec![]
×
UNCOV
53
    }
×
54

55
    /// Returns the named parameters of the table function, if any.
56
    fn named_parameters() -> Vec<(CString, LogicalType)> {
36✔
57
        // By default, we don't have any named parameters.
58
        vec![]
36✔
59
    }
36✔
60

61
    /// This function is used for determining the schema of a table producing function and
62
    /// returning bind data.
63
    fn bind(input: &BindInput, result: &mut BindResult) -> VortexResult<Self::BindData>;
64

65
    /// The function is called during query execution and is responsible for producing the output
66
    fn scan(
67
        bind_data: &Self::BindData,
68
        init_local: &mut Self::LocalState,
69
        init_global: &mut Self::GlobalState,
70
        chunk: &mut DataChunk,
71
    ) -> VortexResult<()>;
72

73
    /// Initialize the global operator state of the function.
74
    ///
75
    /// The global operator state is used to keep track of the progress in the table function and
76
    /// is shared between all threads working on the table function.
77
    fn init_global(input: &TableInitInput<Self>) -> VortexResult<Self::GlobalState>;
78

79
    /// Initialize the local operator state of the function.
80
    ///
81
    /// The local operator state is used to keep track of the progress in the table function and
82
    /// is thread-local.
83
    fn init_local(
84
        init: &TableInitInput<Self>,
85
        global: &mut Self::GlobalState,
86
    ) -> VortexResult<Self::LocalState>;
87

88
    /// Pushes down a filter expression to the table function.
89
    ///
90
    /// Returns `true` if the filter was successfully pushed down (and stored on the bind data),
91
    /// or `false` if the filter could not be pushed down. In which case, the filter will be
92
    /// applied later in the query plan.
93
    fn pushdown_complex_filter(
×
94
        _bind_data: &mut Self::BindData,
×
95
        _expr: &Expression,
×
96
    ) -> VortexResult<bool> {
×
UNCOV
97
        Ok(false)
×
UNCOV
98
    }
×
99

100
    /// Returns the cardinality estimate of the table function.
101
    fn cardinality(_bind_data: &Self::BindData) -> Cardinality {
×
UNCOV
102
        Cardinality::Unknown
×
UNCOV
103
    }
×
104

105
    /// Returns the idx of the current partition being processed by a local threa.
106
    /// This *must* be globally unique.
107
    fn partition_data(
108
        _bind_data: &Self::BindData,
109
        _global_init_data: &mut Self::GlobalState,
110
        _local_init_data: &mut Self::LocalState,
111
    ) -> VortexResult<u64>;
112

113
    // TODO(ngates): there are many more callbacks that can be configured.
114
}
115

116
pub enum Cardinality {
117
    /// Completely unknown cardinality.
118
    Unknown,
119
    /// An estimate of the number of rows that will be returned by the table function.
120
    Estimate(u64),
121
    /// Will not return more than this number of rows.
122
    Maximum(u64),
123
}
124

125
impl Connection {
126
    pub fn register_table_function<T: TableFunction>(&self, name: &CStr) -> VortexResult<()> {
36✔
127
        // Set up the parameters.
128
        let parameters = T::parameters();
36✔
129
        let parameter_ptrs = parameters
36✔
130
            .iter()
36✔
131
            .map(|logical_type| logical_type.as_ptr())
36✔
132
            .collect::<Vec<_>>();
36✔
133

134
        let param_names = T::named_parameters();
36✔
135
        let (param_names_ptrs, param_types_ptr) = param_names
36✔
136
            .into_iter()
36✔
137
            .map(|(name, logical_type)| (name.as_ptr(), logical_type.as_ptr()))
36✔
138
            .unzip::<_, _, Vec<_>, Vec<_>>();
36✔
139

140
        let vtab = cpp::duckdb_vx_tfunc_vtab_t {
36✔
141
            name: name.as_ptr(),
36✔
142
            parameters: parameter_ptrs.as_ptr(),
36✔
143
            parameter_count: parameters.len() as _,
36✔
144
            named_parameter_names: param_names_ptrs.as_ptr(),
36✔
145
            named_parameter_types: param_types_ptr.as_ptr(),
36✔
146
            named_parameter_count: param_names_ptrs.len() as _,
36✔
147
            bind: Some(bind_callback::<T>),
36✔
148
            bind_data_clone: Some(bind_data_clone_callback::<T>),
36✔
149
            init_global: Some(init_global_callback::<T>),
36✔
150
            init_local: Some(init_local_callback::<T>),
36✔
151
            function: Some(function::<T>),
36✔
152
            statistics: ptr::null_mut::<c_void>(),
36✔
153
            cardinality: Some(cardinality_callback::<T>),
36✔
154
            pushdown_complex_filter: Some(pushdown_complex_filter_callback::<T>),
36✔
155
            pushdown_expression: ptr::null_mut::<c_void>(),
36✔
156
            table_scan_progress: ptr::null_mut::<c_void>(),
36✔
157
            get_partition_data: Some(get_partition_data_callback::<T>),
36✔
158
            projection_pushdown: T::PROJECTION_PUSHDOWN,
36✔
159
            filter_pushdown: T::FILTER_PUSHDOWN,
36✔
160
            filter_prune: T::FILTER_PRUNE,
36✔
161
            sampling_pushdown: false,
36✔
162
            late_materialization: false,
36✔
163
        };
36✔
164

165
        duckdb_try!(
36✔
166
            unsafe { cpp::duckdb_vx_tfunc_register(self.as_ptr(), &raw const vtab) },
36✔
UNCOV
167
            "Failed to register table function '{}'",
×
UNCOV
168
            name.to_string_lossy()
×
169
        );
170

171
        Ok(())
36✔
172
    }
36✔
173
}
174

175
/// The native function callback for a table function.
176
unsafe extern "C-unwind" fn function<T: TableFunction>(
9,145✔
177
    bind_data: *const c_void,
9,145✔
178
    global_init_data: *mut c_void,
9,145✔
179
    local_init_data: *mut c_void,
9,145✔
180
    output: cpp::duckdb_data_chunk,
9,145✔
181
    error_out: *mut cpp::duckdb_vx_error,
9,145✔
182
) {
9,145✔
183
    let bind_data = unsafe { &*(bind_data as *const T::BindData) };
9,145✔
184
    let global_init_data = unsafe { global_init_data.cast::<T::GlobalState>().as_mut() }
9,145✔
185
        .vortex_expect("global_init_data null pointer");
9,145✔
186
    let local_init_data = unsafe { local_init_data.cast::<T::LocalState>().as_mut() }
9,145✔
187
        .vortex_expect("local_init_data null pointer");
9,145✔
188
    let mut data_chunk = unsafe { DataChunk::borrow(output) };
9,145✔
189

190
    match T::scan(
9,145✔
191
        bind_data,
9,145✔
192
        local_init_data,
9,145✔
193
        global_init_data,
9,145✔
194
        &mut data_chunk,
9,145✔
195
    ) {
9,145✔
196
        Ok(()) => {
9,145✔
197
            // The data chunk is already filled by the function.
9,145✔
198
            // No need to do anything here.
9,145✔
199
        }
9,145✔
UNCOV
200
        Err(e) => unsafe {
×
UNCOV
201
            error_out.write(cpp::duckdb_vx_error_create(
×
UNCOV
202
                e.to_string().as_ptr().cast(),
×
UNCOV
203
                e.to_string().len(),
×
UNCOV
204
            ));
×
UNCOV
205
        },
×
206
    }
207
}
9,145✔
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