diff --git a/core/lib.rs b/core/lib.rs index 2f7ab0577..f36072e3a 100644 --- a/core/lib.rs +++ b/core/lib.rs @@ -730,7 +730,7 @@ impl VirtualTable { pub fn open(&self) -> crate::Result { let cursor = unsafe { (self.implementation.open)(self.implementation.ctx) }; - VTabOpaqueCursor::new(cursor) + VTabOpaqueCursor::new(cursor, self.implementation.close) } #[tracing::instrument(skip(cursor))] diff --git a/core/vdbe/mod.rs b/core/vdbe/mod.rs index 2adf438b6..d90504df4 100644 --- a/core/vdbe/mod.rs +++ b/core/vdbe/mod.rs @@ -203,20 +203,34 @@ impl Bitfield { } } -pub struct VTabOpaqueCursor(*const c_void); +type VTabOpaqueCursorCloseFn = unsafe extern "C" fn(*const c_void) -> limbo_ext::ResultCode; + +pub struct VTabOpaqueCursor { + cursor: *const c_void, + close: VTabOpaqueCursorCloseFn, +} impl VTabOpaqueCursor { - pub fn new(cursor: *const c_void) -> Result { + pub fn new(cursor: *const c_void, close: VTabOpaqueCursorCloseFn) -> Result { if cursor.is_null() { return Err(LimboError::InternalError( "VTabOpaqueCursor: cursor is null".into(), )); } - Ok(Self(cursor)) + Ok(Self { cursor, close }) } pub fn as_ptr(&self) -> *const c_void { - self.0 + self.cursor + } +} + +impl Drop for VTabOpaqueCursor { + fn drop(&mut self) { + let result = unsafe { (self.close)(self.cursor) }; + if !result.is_ok() { + tracing::error!("Failed to close virtual table cursor"); + } } } diff --git a/extensions/core/src/vtabs.rs b/extensions/core/src/vtabs.rs index 5d86457f7..de5794c6e 100644 --- a/extensions/core/src/vtabs.rs +++ b/extensions/core/src/vtabs.rs @@ -15,6 +15,7 @@ pub struct VTabModuleImpl { pub name: *const c_char, pub create_schema: VtabFnCreateSchema, pub open: VtabFnOpen, + pub close: VtabFnClose, pub filter: VtabFnFilter, pub column: VtabFnColumn, pub next: VtabFnNext, @@ -44,6 +45,8 @@ pub type VtabFnCreateSchema = unsafe extern "C" fn(args: *const Value, argc: i32 pub type VtabFnOpen = unsafe extern "C" fn(*const c_void) -> *const c_void; +pub type VtabFnClose = unsafe extern "C" fn(cursor: *const c_void) -> ResultCode; + pub type VtabFnFilter = unsafe extern "C" fn( cursor: *const c_void, argc: i32, @@ -134,6 +137,9 @@ pub trait VTabCursor: Sized { fn column(&self, idx: u32) -> Result; fn eof(&self) -> bool; fn next(&mut self) -> ResultCode; + fn close(&self) -> ResultCode { + ResultCode::OK + } } #[repr(u8)] diff --git a/macros/src/lib.rs b/macros/src/lib.rs index d47101589..2d6694e10 100644 --- a/macros/src/lib.rs +++ b/macros/src/lib.rs @@ -448,6 +448,7 @@ pub fn derive_vtab_module(input: TokenStream) -> TokenStream { let register_fn_name = format_ident!("register_{}", struct_name); let create_schema_fn_name = format_ident!("create_schema_{}", struct_name); let open_fn_name = format_ident!("open_{}", struct_name); + let close_fn_name = format_ident!("close_{}", struct_name); let filter_fn_name = format_ident!("filter_{}", struct_name); let column_fn_name = format_ident!("column_{}", struct_name); let next_fn_name = format_ident!("next_{}", struct_name); @@ -486,6 +487,17 @@ pub fn derive_vtab_module(input: TokenStream) -> TokenStream { } } + #[no_mangle] + unsafe extern "C" fn #close_fn_name( + cursor: *const ::std::ffi::c_void + ) -> ::limbo_ext::ResultCode { + if cursor.is_null() { + return ::limbo_ext::ResultCode::Error; + } + let boxed_cursor = ::std::boxed::Box::from_raw(cursor as *mut <#struct_name as ::limbo_ext::VTabModule>::VCursor); + boxed_cursor.close() + } + #[no_mangle] unsafe extern "C" fn #filter_fn_name( cursor: *const ::std::ffi::c_void, @@ -649,6 +661,7 @@ pub fn derive_vtab_module(input: TokenStream) -> TokenStream { name: name_c, create_schema: Self::#create_schema_fn_name, open: Self::#open_fn_name, + close: Self::#close_fn_name, filter: Self::#filter_fn_name, column: Self::#column_fn_name, next: Self::#next_fn_name,