Skip to content

Commit d48b967

Browse files
authored
feat: add named parameter support to virtual tables (#250)
* feat: add named parameter support * safely handle missing parameters
1 parent 9af377a commit d48b967

File tree

2 files changed

+100
-8
lines changed

2 files changed

+100
-8
lines changed

Diff for: src/vtab/function.rs

+50-8
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,13 @@
11
use super::{
22
ffi::{
3-
duckdb_bind_add_result_column, duckdb_bind_get_extra_info, duckdb_bind_get_parameter,
4-
duckdb_bind_get_parameter_count, duckdb_bind_info, duckdb_bind_set_bind_data, duckdb_bind_set_cardinality,
5-
duckdb_bind_set_error, duckdb_create_table_function, duckdb_data_chunk, duckdb_delete_callback_t,
6-
duckdb_destroy_table_function, duckdb_table_function, duckdb_table_function_add_parameter,
7-
duckdb_table_function_init_t, duckdb_table_function_set_bind, duckdb_table_function_set_extra_info,
8-
duckdb_table_function_set_function, duckdb_table_function_set_init, duckdb_table_function_set_local_init,
9-
duckdb_table_function_set_name, duckdb_table_function_supports_projection_pushdown, idx_t,
3+
duckdb_bind_add_result_column, duckdb_bind_get_extra_info, duckdb_bind_get_named_parameter,
4+
duckdb_bind_get_parameter, duckdb_bind_get_parameter_count, duckdb_bind_info, duckdb_bind_set_bind_data,
5+
duckdb_bind_set_cardinality, duckdb_bind_set_error, duckdb_create_table_function, duckdb_data_chunk,
6+
duckdb_delete_callback_t, duckdb_destroy_table_function, duckdb_table_function,
7+
duckdb_table_function_add_named_parameter, duckdb_table_function_add_parameter, duckdb_table_function_init_t,
8+
duckdb_table_function_set_bind, duckdb_table_function_set_extra_info, duckdb_table_function_set_function,
9+
duckdb_table_function_set_init, duckdb_table_function_set_local_init, duckdb_table_function_set_name,
10+
duckdb_table_function_supports_projection_pushdown, idx_t,
1011
},
1112
LogicalType, Value,
1213
};
@@ -64,8 +65,36 @@ impl BindInfo {
6465
/// * `index`: The index of the parameter to get
6566
///
6667
/// returns: The value of the parameter
68+
///
69+
/// # Panics
70+
/// If requested parameter is out of range for function definition
6771
pub fn get_parameter(&self, param_index: u64) -> Value {
68-
unsafe { Value::from(duckdb_bind_get_parameter(self.ptr, param_index)) }
72+
unsafe {
73+
let ptr = duckdb_bind_get_parameter(self.ptr, param_index);
74+
if ptr.is_null() {
75+
panic!("{} is out of range for function definition", param_index);
76+
} else {
77+
Value::from(ptr)
78+
}
79+
}
80+
}
81+
82+
/// Retrieves the named parameter with the given name.
83+
///
84+
/// # Arguments
85+
/// * `name`: The name of the parameter to get
86+
///
87+
/// returns: The value of the parameter
88+
pub fn get_named_parameter(&self, name: &str) -> Option<Value> {
89+
unsafe {
90+
let name = &CString::new(name).unwrap();
91+
let ptr = duckdb_bind_get_named_parameter(self.ptr, name.as_ptr());
92+
if ptr.is_null() {
93+
None
94+
} else {
95+
Some(Value::from(ptr))
96+
}
97+
}
6998
}
7099

71100
/// Sets the cardinality estimate for the table function, used for optimization.
@@ -204,6 +233,19 @@ impl TableFunction {
204233
self
205234
}
206235

236+
/// Adds a named parameter to the table function.
237+
///
238+
/// # Arguments
239+
/// * `name`: The name of the parameter to add.
240+
/// * `logical_type`: The type of the parameter to add.
241+
pub fn add_named_parameter(&self, name: &str, logical_type: &LogicalType) -> &Self {
242+
unsafe {
243+
let string = CString::new(name).unwrap();
244+
duckdb_table_function_add_named_parameter(self.ptr, string.as_ptr(), logical_type.ptr);
245+
}
246+
self
247+
}
248+
207249
/// Sets the main function of the table function
208250
///
209251
/// # Arguments

Diff for: src/vtab/mod.rs

+50
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,11 @@ pub trait VTab: Sized {
8181
fn parameters() -> Option<Vec<LogicalType>> {
8282
None
8383
}
84+
/// The named parameters of the table function
85+
/// default is None
86+
fn named_parameters() -> Option<Vec<(String, LogicalType)>> {
87+
None
88+
}
8489
}
8590

8691
unsafe extern "C" fn func<T>(info: duckdb_function_info, output: duckdb_data_chunk)
@@ -135,6 +140,9 @@ impl Connection {
135140
for ty in T::parameters().unwrap_or_default() {
136141
table_function.add_parameter(&ty);
137142
}
143+
for (name, ty) in T::named_parameters().unwrap_or_default() {
144+
table_function.add_named_parameter(&name, &ty);
145+
}
138146
self.db.borrow_mut().register_table_function(table_function)
139147
}
140148
}
@@ -232,13 +240,55 @@ mod test {
232240
}
233241
}
234242

243+
struct HelloWithNamedVTab {}
244+
impl VTab for HelloWithNamedVTab {
245+
type InitData = HelloInitData;
246+
type BindData = HelloBindData;
247+
248+
fn bind(bind: &BindInfo, data: *mut HelloBindData) -> Result<(), Box<dyn Error>> {
249+
bind.add_result_column("column0", LogicalType::new(LogicalTypeId::Varchar));
250+
let param = bind.get_named_parameter("name").unwrap().to_string();
251+
assert!(bind.get_named_parameter("unknown_name").is_none());
252+
unsafe {
253+
(*data).name = CString::new(param).unwrap().into_raw();
254+
}
255+
Ok(())
256+
}
257+
258+
fn init(init_info: &InitInfo, data: *mut HelloInitData) -> Result<(), Box<dyn Error>> {
259+
HelloVTab::init(init_info, data)
260+
}
261+
262+
fn func(func: &FunctionInfo, output: &mut DataChunk) -> Result<(), Box<dyn Error>> {
263+
HelloVTab::func(func, output)
264+
}
265+
266+
fn named_parameters() -> Option<Vec<(String, LogicalType)>> {
267+
Some(vec![("name".to_string(), LogicalType::new(LogicalTypeId::Varchar))])
268+
}
269+
}
270+
235271
#[test]
236272
fn test_table_function() -> Result<(), Box<dyn Error>> {
237273
let conn = Connection::open_in_memory()?;
238274
conn.register_table_function::<HelloVTab>("hello")?;
239275

240276
let val = conn.query_row("select * from hello('duckdb')", [], |row| <(String,)>::try_from(row))?;
241277
assert_eq!(val, ("Hello duckdb".to_string(),));
278+
279+
Ok(())
280+
}
281+
282+
#[test]
283+
fn test_named_table_function() -> Result<(), Box<dyn Error>> {
284+
let conn = Connection::open_in_memory()?;
285+
conn.register_table_function::<HelloWithNamedVTab>("hello_named")?;
286+
287+
let val = conn.query_row("select * from hello_named(name = 'duckdb')", [], |row| {
288+
<(String,)>::try_from(row)
289+
})?;
290+
assert_eq!(val, ("Hello duckdb".to_string(),));
291+
242292
Ok(())
243293
}
244294

0 commit comments

Comments
 (0)