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

vortex-data / vortex / 17045275660

18 Aug 2025 03:37PM UTC coverage: 87.914%. Remained the same
17045275660

Pull #4272

github

web-flow
Merge 1d60c1d1a into cb2220961
Pull Request #4272: chore[vortex-duckdb]: duckdb `explain` custom display

23 of 31 new or added lines in 2 files covered. (74.19%)

17 existing lines in 2 files now uncovered.

56584 of 64363 relevant lines covered (87.91%)

628878.94 hits per line

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

84.27
/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::cpp::duckdb_vx_client_context;
19
use crate::duckdb::LogicalType;
20
use crate::duckdb::client_context::ClientContext;
21
use crate::duckdb::connection::Connection;
22
use crate::duckdb::data_chunk::DataChunk;
23
use crate::duckdb::expr::Expression;
24
use crate::duckdb::table_function::cardinality::cardinality_callback;
25
use crate::duckdb::table_function::partition::get_partition_data_callback;
26
use crate::duckdb::table_function::pushdown_complex_filter::pushdown_complex_filter_callback;
27
use crate::{cpp, duckdb_try};
28

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

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

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

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

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

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

63
    /// This function is used for determining the schema of a table producing function and
64
    /// returning bind data.
65
    fn bind(
66
        client_context: &ClientContext,
67
        input: &BindInput,
68
        result: &mut BindResult,
69
    ) -> VortexResult<Self::BindData>;
70

71
    /// The function is called during query execution and is responsible for producing the output
72
    fn scan(
73
        client_context: &ClientContext,
74
        bind_data: &Self::BindData,
75
        init_local: &mut Self::LocalState,
76
        init_global: &mut Self::GlobalState,
77
        chunk: &mut DataChunk,
78
    ) -> VortexResult<()>;
79

80
    /// Initialize the global operator state of the function.
81
    ///
82
    /// The global operator state is used to keep track of the progress in the table function and
83
    /// is shared between all threads working on the table function.
84
    fn init_global(input: &TableInitInput<Self>) -> VortexResult<Self::GlobalState>;
85

86
    /// Initialize the local operator state of the function.
87
    ///
88
    /// The local operator state is used to keep track of the progress in the table function and
89
    /// is thread-local.
90
    fn init_local(
91
        init: &TableInitInput<Self>,
92
        global: &mut Self::GlobalState,
93
    ) -> VortexResult<Self::LocalState>;
94

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

107
    /// Returns the cardinality estimate of the table function.
108
    fn cardinality(_bind_data: &Self::BindData) -> Cardinality {
2✔
109
        Cardinality::Unknown
2✔
110
    }
2✔
111

112
    /// Returns the idx of the current partition being processed by a local threa.
113
    /// This *must* be globally unique.
114
    fn partition_data(
115
        _bind_data: &Self::BindData,
116
        _global_init_data: &mut Self::GlobalState,
117
        _local_init_data: &mut Self::LocalState,
118
    ) -> VortexResult<u64>;
119

120
    // TODO(ngates): there are many more callbacks that can be configured.
121
}
122

123
pub enum Cardinality {
124
    /// Completely unknown cardinality.
125
    Unknown,
126
    /// An estimate of the number of rows that will be returned by the table function.
127
    Estimate(u64),
128
    /// Will not return more than this number of rows.
129
    Maximum(u64),
130
}
131

132
impl Connection {
133
    pub fn register_table_function<T: TableFunction>(&self, name: &CStr) -> VortexResult<()> {
45✔
134
        // Set up the parameters.
135
        let parameters = T::parameters();
45✔
136
        let parameter_ptrs = parameters
45✔
137
            .iter()
45✔
138
            .map(|logical_type| logical_type.as_ptr())
45✔
139
            .collect::<Vec<_>>();
45✔
140

141
        let param_names = T::named_parameters();
45✔
142
        let (param_names_ptrs, param_types_ptr) = param_names
45✔
143
            .into_iter()
45✔
144
            .map(|(name, logical_type)| (name.as_ptr(), logical_type.as_ptr()))
45✔
145
            .unzip::<_, _, Vec<_>, Vec<_>>();
45✔
146

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

172
        duckdb_try!(
45✔
173
            unsafe { cpp::duckdb_vx_tfunc_register(self.as_ptr(), &raw const vtab) },
45✔
UNCOV
174
            "Failed to register table function '{}'",
×
UNCOV
175
            name.to_string_lossy()
×
176
        );
177

178
        Ok(())
45✔
179
    }
45✔
180
}
181

182
/// The native function callback for a table function.
183
unsafe extern "C-unwind" fn function<T: TableFunction>(
13,694✔
184
    duckdb_client_context: duckdb_vx_client_context,
13,694✔
185
    bind_data: *const c_void,
13,694✔
186
    global_init_data: *mut c_void,
13,694✔
187
    local_init_data: *mut c_void,
13,694✔
188
    output: cpp::duckdb_data_chunk,
13,694✔
189
    error_out: *mut cpp::duckdb_vx_error,
13,694✔
190
) {
13,694✔
191
    let client_context = unsafe { ClientContext::borrow(duckdb_client_context) };
13,694✔
192
    let bind_data = unsafe { &*(bind_data as *const T::BindData) };
13,694✔
193
    let global_init_data = unsafe { global_init_data.cast::<T::GlobalState>().as_mut() }
13,694✔
194
        .vortex_expect("global_init_data null pointer");
13,694✔
195
    let local_init_data = unsafe { local_init_data.cast::<T::LocalState>().as_mut() }
13,694✔
196
        .vortex_expect("local_init_data null pointer");
13,694✔
197
    let mut data_chunk = unsafe { DataChunk::borrow(output) };
13,694✔
198

199
    match T::scan(
13,694✔
200
        &client_context,
13,694✔
201
        bind_data,
13,694✔
202
        local_init_data,
13,694✔
203
        global_init_data,
13,694✔
204
        &mut data_chunk,
13,694✔
205
    ) {
13,694✔
206
        Ok(()) => {
13,694✔
207
            // The data chunk is already filled by the function.
13,694✔
208
            // No need to do anything here.
13,694✔
209
        }
13,694✔
NEW
210
        Err(e) => unsafe {
×
NEW
211
            error_out.write(cpp::duckdb_vx_error_create(
×
NEW
212
                e.to_string().as_ptr().cast(),
×
NEW
213
                e.to_string().len(),
×
NEW
214
            ));
×
NEW
215
        },
×
216
    }
217
}
13,694✔
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