VTabCursor lifetime should be bound to VTab lifetime

```c
struct sqlite3_vtab_cursor {
  sqlite3_vtab *pVtab;      /* Virtual table of this cursor */
```
It seems that we need to introduce a lifetime on `VTab` trait
to express such constraint:
https://users.rust-lang.org/t/associated-type-with-lifetime-bound/7256
But there may be another/new way to do it.
This commit is contained in:
gwenn 2020-06-01 09:48:49 +02:00
parent 48a15857fb
commit 2af75d1f13
5 changed files with 74 additions and 56 deletions

View File

@ -27,6 +27,7 @@
//! ``` //! ```
use std::default::Default; use std::default::Default;
use std::marker::PhantomData;
use std::os::raw::{c_char, c_int, c_void}; use std::os::raw::{c_char, c_int, c_void};
use std::rc::Rc; use std::rc::Rc;
@ -72,9 +73,9 @@ struct ArrayTab {
base: ffi::sqlite3_vtab, base: ffi::sqlite3_vtab,
} }
unsafe impl VTab for ArrayTab { unsafe impl<'vtab> VTab<'vtab> for ArrayTab {
type Aux = (); type Aux = ();
type Cursor = ArrayTabCursor; type Cursor = ArrayTabCursor<'vtab>;
fn connect( fn connect(
_: &mut VTabConnection, _: &mut VTabConnection,
@ -118,28 +119,30 @@ unsafe impl VTab for ArrayTab {
Ok(()) Ok(())
} }
fn open(&self) -> Result<ArrayTabCursor> { fn open(&'vtab self) -> Result<ArrayTabCursor<'vtab>> {
Ok(ArrayTabCursor::new()) Ok(ArrayTabCursor::new())
} }
} }
/// A cursor for the Array virtual table /// A cursor for the Array virtual table
#[repr(C)] #[repr(C)]
struct ArrayTabCursor { struct ArrayTabCursor<'vtab> {
/// Base class. Must be first /// Base class. Must be first
base: ffi::sqlite3_vtab_cursor, base: ffi::sqlite3_vtab_cursor,
/// The rowid /// The rowid
row_id: i64, row_id: i64,
/// Pointer to the array of values ("pointer") /// Pointer to the array of values ("pointer")
ptr: Option<Array>, ptr: Option<Array>,
phantom: PhantomData<&'vtab ArrayTab>,
} }
impl ArrayTabCursor { impl ArrayTabCursor<'_> {
fn new() -> ArrayTabCursor { fn new<'vtab>() -> ArrayTabCursor<'vtab> {
ArrayTabCursor { ArrayTabCursor {
base: ffi::sqlite3_vtab_cursor::default(), base: ffi::sqlite3_vtab_cursor::default(),
row_id: 0, row_id: 0,
ptr: None, ptr: None,
phantom: PhantomData,
} }
} }
@ -150,7 +153,7 @@ impl ArrayTabCursor {
} }
} }
} }
unsafe impl VTabCursor for ArrayTabCursor { unsafe impl VTabCursor for ArrayTabCursor<'_> {
fn filter(&mut self, idx_num: c_int, _idx_str: Option<&str>, args: &Values<'_>) -> Result<()> { fn filter(&mut self, idx_num: c_int, _idx_str: Option<&str>, args: &Values<'_>) -> Result<()> {
if idx_num > 0 { if idx_num > 0 {
self.ptr = args.get_array(0)?; self.ptr = args.get_array(0)?;

View File

@ -22,6 +22,7 @@
//! } //! }
//! ``` //! ```
use std::fs::File; use std::fs::File;
use std::marker::PhantomData;
use std::os::raw::c_int; use std::os::raw::c_int;
use std::path::Path; use std::path::Path;
use std::str; use std::str;
@ -95,9 +96,9 @@ impl CSVTab {
} }
} }
unsafe impl VTab for CSVTab { unsafe impl<'vtab> VTab<'vtab> for CSVTab {
type Aux = (); type Aux = ();
type Cursor = CSVTabCursor; type Cursor = CSVTabCursor<'vtab>;
fn connect( fn connect(
_: &mut VTabConnection, _: &mut VTabConnection,
@ -258,16 +259,16 @@ unsafe impl VTab for CSVTab {
Ok(()) Ok(())
} }
fn open(&self) -> Result<CSVTabCursor> { fn open(&'vtab self) -> Result<CSVTabCursor<'vtab>> {
Ok(CSVTabCursor::new(self.reader()?)) Ok(CSVTabCursor::new(self.reader()?))
} }
} }
impl CreateVTab for CSVTab {} impl CreateVTab<'_> for CSVTab {}
/// A cursor for the CSV virtual table /// A cursor for the CSV virtual table
#[repr(C)] #[repr(C)]
struct CSVTabCursor { struct CSVTabCursor<'vtab> {
/// Base class. Must be first /// Base class. Must be first
base: ffi::sqlite3_vtab_cursor, base: ffi::sqlite3_vtab_cursor,
/// The CSV reader object /// The CSV reader object
@ -277,16 +278,18 @@ struct CSVTabCursor {
/// Values of the current row /// Values of the current row
cols: csv::StringRecord, cols: csv::StringRecord,
eof: bool, eof: bool,
phantom: PhantomData<&'vtab CSVTab>,
} }
impl CSVTabCursor { impl CSVTabCursor<'_> {
fn new(reader: csv::Reader<File>) -> CSVTabCursor { fn new<'vtab>(reader: csv::Reader<File>) -> CSVTabCursor<'vtab> {
CSVTabCursor { CSVTabCursor {
base: ffi::sqlite3_vtab_cursor::default(), base: ffi::sqlite3_vtab_cursor::default(),
reader, reader,
row_number: 0, row_number: 0,
cols: csv::StringRecord::new(), cols: csv::StringRecord::new(),
eof: false, eof: false,
phantom: PhantomData,
} }
} }
@ -296,7 +299,7 @@ impl CSVTabCursor {
} }
} }
unsafe impl VTabCursor for CSVTabCursor { unsafe impl VTabCursor for CSVTabCursor<'_> {
// Only a full table scan is supported. So `filter` simply rewinds to // Only a full table scan is supported. So `filter` simply rewinds to
// the beginning. // the beginning.
fn filter( fn filter(

View File

@ -61,13 +61,13 @@ use crate::{str_to_cstring, Connection, Error, InnerConnection, Result};
/// ///
/// (See [SQLite doc](https://sqlite.org/c3ref/module.html)) /// (See [SQLite doc](https://sqlite.org/c3ref/module.html))
#[repr(transparent)] #[repr(transparent)]
pub struct Module<T: VTab> { pub struct Module<'vtab, T: VTab<'vtab>> {
base: ffi::sqlite3_module, base: ffi::sqlite3_module,
phantom: PhantomData<T>, phantom: PhantomData<&'vtab T>,
} }
unsafe impl<T: VTab> Send for Module<T> {} unsafe impl<'vtab, T: VTab<'vtab>> Send for Module<'vtab, T> {}
unsafe impl<T: VTab> Sync for Module<T> {} unsafe impl<'vtab, T: VTab<'vtab>> Sync for Module<'vtab, T> {}
union ModuleZeroHack { union ModuleZeroHack {
bytes: [u8; std::mem::size_of::<ffi::sqlite3_module>()], bytes: [u8; std::mem::size_of::<ffi::sqlite3_module>()],
@ -87,7 +87,7 @@ const ZERO_MODULE: ffi::sqlite3_module = unsafe {
/// `feature = "vtab"` Create a read-only virtual table implementation. /// `feature = "vtab"` Create a read-only virtual table implementation.
/// ///
/// Step 2 of [Creating New Virtual Table Implementations](https://sqlite.org/vtab.html#creating_new_virtual_table_implementations). /// Step 2 of [Creating New Virtual Table Implementations](https://sqlite.org/vtab.html#creating_new_virtual_table_implementations).
pub fn read_only_module<T: CreateVTab>() -> &'static Module<T> { pub fn read_only_module<'vtab, T: CreateVTab<'vtab>>() -> &'static Module<'vtab, T> {
// The xConnect and xCreate methods do the same thing, but they must be // The xConnect and xCreate methods do the same thing, but they must be
// different so that the virtual table is not an eponymous virtual table. // different so that the virtual table is not an eponymous virtual table.
&Module { &Module {
@ -118,14 +118,14 @@ pub fn read_only_module<T: CreateVTab>() -> &'static Module<T> {
xRollbackTo: None, xRollbackTo: None,
..ZERO_MODULE ..ZERO_MODULE
}, },
phantom: PhantomData::<T>, phantom: PhantomData::<&'vtab T>,
} }
} }
/// `feature = "vtab"` Create an eponymous only virtual table implementation. /// `feature = "vtab"` Create an eponymous only virtual table implementation.
/// ///
/// Step 2 of [Creating New Virtual Table Implementations](https://sqlite.org/vtab.html#creating_new_virtual_table_implementations). /// Step 2 of [Creating New Virtual Table Implementations](https://sqlite.org/vtab.html#creating_new_virtual_table_implementations).
pub fn eponymous_only_module<T: VTab>() -> &'static Module<T> { pub fn eponymous_only_module<'vtab, T: VTab<'vtab>>() -> &'static Module<'vtab, T> {
// A virtual table is eponymous if its xCreate method is the exact same function // A virtual table is eponymous if its xCreate method is the exact same function
// as the xConnect method For eponymous-only virtual tables, the xCreate // as the xConnect method For eponymous-only virtual tables, the xCreate
// method is NULL // method is NULL
@ -157,7 +157,7 @@ pub fn eponymous_only_module<T: VTab>() -> &'static Module<T> {
xRollbackTo: None, xRollbackTo: None,
..ZERO_MODULE ..ZERO_MODULE
}, },
phantom: PhantomData::<T>, phantom: PhantomData::<&'vtab T>,
} }
} }
@ -204,7 +204,7 @@ impl VTabConnection {
/// ``` /// ```
/// ///
/// (See [SQLite doc](https://sqlite.org/c3ref/vtab.html)) /// (See [SQLite doc](https://sqlite.org/c3ref/vtab.html))
pub unsafe trait VTab: Sized { pub unsafe trait VTab<'vtab>: Sized {
/// Client data passed to `Connection::create_module`. /// Client data passed to `Connection::create_module`.
type Aux; type Aux;
/// Specific cursor implementation /// Specific cursor implementation
@ -225,13 +225,13 @@ pub unsafe trait VTab: Sized {
/// Create a new cursor used for accessing a virtual table. /// Create a new cursor used for accessing a virtual table.
/// (See [SQLite doc](https://sqlite.org/vtab.html#the_xopen_method)) /// (See [SQLite doc](https://sqlite.org/vtab.html#the_xopen_method))
fn open(&self) -> Result<Self::Cursor>; fn open(&'vtab self) -> Result<Self::Cursor>;
} }
/// `feature = "vtab"` Non-eponymous virtual table instance trait. /// `feature = "vtab"` Non-eponymous virtual table instance trait.
/// ///
/// (See [SQLite doc](https://sqlite.org/c3ref/vtab.html)) /// (See [SQLite doc](https://sqlite.org/c3ref/vtab.html))
pub trait CreateVTab: VTab { pub trait CreateVTab<'vtab>: VTab<'vtab> {
/// Create a new instance of a virtual table in response to a CREATE VIRTUAL /// Create a new instance of a virtual table in response to a CREATE VIRTUAL
/// TABLE statement. The `db` parameter is a pointer to the SQLite /// TABLE statement. The `db` parameter is a pointer to the SQLite
/// database connection that is executing the CREATE VIRTUAL TABLE /// database connection that is executing the CREATE VIRTUAL TABLE
@ -607,10 +607,10 @@ impl Connection {
/// ///
/// Step 3 of [Creating New Virtual Table /// Step 3 of [Creating New Virtual Table
/// Implementations](https://sqlite.org/vtab.html#creating_new_virtual_table_implementations). /// Implementations](https://sqlite.org/vtab.html#creating_new_virtual_table_implementations).
pub fn create_module<T: VTab>( pub fn create_module<'vtab, T: VTab<'vtab>>(
&self, &self,
module_name: &str, module_name: &str,
module: &'static Module<T>, module: &'static Module<'vtab, T>,
aux: Option<T::Aux>, aux: Option<T::Aux>,
) -> Result<()> { ) -> Result<()> {
self.db.borrow_mut().create_module(module_name, module, aux) self.db.borrow_mut().create_module(module_name, module, aux)
@ -618,10 +618,10 @@ impl Connection {
} }
impl InnerConnection { impl InnerConnection {
fn create_module<T: VTab>( fn create_module<'vtab, T: VTab<'vtab>>(
&mut self, &mut self,
module_name: &str, module_name: &str,
module: &'static Module<T>, module: &'static Module<'vtab, T>,
aux: Option<T::Aux>, aux: Option<T::Aux>,
) -> Result<()> { ) -> Result<()> {
let c_name = str_to_cstring(module_name)?; let c_name = str_to_cstring(module_name)?;
@ -703,7 +703,7 @@ unsafe extern "C" fn free_boxed_value<T>(p: *mut c_void) {
let _: Box<T> = Box::from_raw(p as *mut T); let _: Box<T> = Box::from_raw(p as *mut T);
} }
unsafe extern "C" fn rust_create<T>( unsafe extern "C" fn rust_create<'vtab, T>(
db: *mut ffi::sqlite3, db: *mut ffi::sqlite3,
aux: *mut c_void, aux: *mut c_void,
argc: c_int, argc: c_int,
@ -712,7 +712,7 @@ unsafe extern "C" fn rust_create<T>(
err_msg: *mut *mut c_char, err_msg: *mut *mut c_char,
) -> c_int ) -> c_int
where where
T: CreateVTab, T: CreateVTab<'vtab>,
{ {
use std::ffi::CStr; use std::ffi::CStr;
@ -755,7 +755,7 @@ where
} }
} }
unsafe extern "C" fn rust_connect<T>( unsafe extern "C" fn rust_connect<'vtab, T>(
db: *mut ffi::sqlite3, db: *mut ffi::sqlite3,
aux: *mut c_void, aux: *mut c_void,
argc: c_int, argc: c_int,
@ -764,7 +764,7 @@ unsafe extern "C" fn rust_connect<T>(
err_msg: *mut *mut c_char, err_msg: *mut *mut c_char,
) -> c_int ) -> c_int
where where
T: VTab, T: VTab<'vtab>,
{ {
use std::ffi::CStr; use std::ffi::CStr;
@ -807,12 +807,12 @@ where
} }
} }
unsafe extern "C" fn rust_best_index<T>( unsafe extern "C" fn rust_best_index<'vtab, T>(
vtab: *mut ffi::sqlite3_vtab, vtab: *mut ffi::sqlite3_vtab,
info: *mut ffi::sqlite3_index_info, info: *mut ffi::sqlite3_index_info,
) -> c_int ) -> c_int
where where
T: VTab, T: VTab<'vtab>,
{ {
let vt = vtab as *mut T; let vt = vtab as *mut T;
let mut idx_info = IndexInfo(info); let mut idx_info = IndexInfo(info);
@ -831,9 +831,9 @@ where
} }
} }
unsafe extern "C" fn rust_disconnect<T>(vtab: *mut ffi::sqlite3_vtab) -> c_int unsafe extern "C" fn rust_disconnect<'vtab, T>(vtab: *mut ffi::sqlite3_vtab) -> c_int
where where
T: VTab, T: VTab<'vtab>,
{ {
if vtab.is_null() { if vtab.is_null() {
return ffi::SQLITE_OK; return ffi::SQLITE_OK;
@ -843,9 +843,9 @@ where
ffi::SQLITE_OK ffi::SQLITE_OK
} }
unsafe extern "C" fn rust_destroy<T>(vtab: *mut ffi::sqlite3_vtab) -> c_int unsafe extern "C" fn rust_destroy<'vtab, T>(vtab: *mut ffi::sqlite3_vtab) -> c_int
where where
T: CreateVTab, T: CreateVTab<'vtab>,
{ {
if vtab.is_null() { if vtab.is_null() {
return ffi::SQLITE_OK; return ffi::SQLITE_OK;
@ -869,12 +869,12 @@ where
} }
} }
unsafe extern "C" fn rust_open<T>( unsafe extern "C" fn rust_open<'vtab, T: 'vtab>(
vtab: *mut ffi::sqlite3_vtab, vtab: *mut ffi::sqlite3_vtab,
pp_cursor: *mut *mut ffi::sqlite3_vtab_cursor, pp_cursor: *mut *mut ffi::sqlite3_vtab_cursor,
) -> c_int ) -> c_int
where where
T: VTab, T: VTab<'vtab>,
{ {
let vt = vtab as *mut T; let vt = vtab as *mut T;
match (*vt).open() { match (*vt).open() {

View File

@ -4,6 +4,7 @@
//! "function"](http://www.sqlite.org/cgi/src/finfo?name=ext/misc/series.c): //! "function"](http://www.sqlite.org/cgi/src/finfo?name=ext/misc/series.c):
//! https://www.sqlite.org/series.html //! https://www.sqlite.org/series.html
use std::default::Default; use std::default::Default;
use std::marker::PhantomData;
use std::os::raw::c_int; use std::os::raw::c_int;
use crate::ffi; use crate::ffi;
@ -49,9 +50,9 @@ struct SeriesTab {
base: ffi::sqlite3_vtab, base: ffi::sqlite3_vtab,
} }
unsafe impl VTab for SeriesTab { unsafe impl<'vtab> VTab<'vtab> for SeriesTab {
type Aux = (); type Aux = ();
type Cursor = SeriesTabCursor; type Cursor = SeriesTabCursor<'vtab>;
fn connect( fn connect(
_: &mut VTabConnection, _: &mut VTabConnection,
@ -151,15 +152,14 @@ unsafe impl VTab for SeriesTab {
Ok(()) Ok(())
} }
fn open(&self) -> Result<SeriesTabCursor> { fn open(&'vtab self) -> Result<SeriesTabCursor<'vtab>> {
Ok(SeriesTabCursor::new()) Ok(SeriesTabCursor::new())
} }
} }
/// A cursor for the Series virtual table /// A cursor for the Series virtual table
#[derive(Default)]
#[repr(C)] #[repr(C)]
struct SeriesTabCursor { struct SeriesTabCursor<'vtab> {
/// Base class. Must be first /// Base class. Must be first
base: ffi::sqlite3_vtab_cursor, base: ffi::sqlite3_vtab_cursor,
/// True to count down rather than up /// True to count down rather than up
@ -174,14 +174,24 @@ struct SeriesTabCursor {
max_value: i64, max_value: i64,
/// Increment ("step") /// Increment ("step")
step: i64, step: i64,
phantom: PhantomData<&'vtab SeriesTab>,
} }
impl SeriesTabCursor { impl SeriesTabCursor<'_> {
fn new() -> SeriesTabCursor { fn new<'vtab>() -> SeriesTabCursor<'vtab> {
SeriesTabCursor::default() SeriesTabCursor {
base: ffi::sqlite3_vtab_cursor::default(),
is_desc: false,
row_id: 0,
value: 0,
min_value: 0,
max_value: 0,
step: 0,
phantom: PhantomData,
} }
} }
unsafe impl VTabCursor for SeriesTabCursor { }
unsafe impl VTabCursor for SeriesTabCursor<'_> {
fn filter(&mut self, idx_num: c_int, _idx_str: Option<&str>, args: &Values<'_>) -> Result<()> { fn filter(&mut self, idx_num: c_int, _idx_str: Option<&str>, args: &Values<'_>) -> Result<()> {
let idx_num = QueryPlanFlags::from_bits_truncate(idx_num); let idx_num = QueryPlanFlags::from_bits_truncate(idx_num);
let mut i = 0; let mut i = 0;

View File

@ -9,6 +9,7 @@ fn test_dummy_module() {
VTabConnection, VTabCursor, Values, VTabConnection, VTabCursor, Values,
}; };
use rusqlite::{version_number, Connection, Result}; use rusqlite::{version_number, Connection, Result};
use std::marker::PhantomData;
use std::os::raw::c_int; use std::os::raw::c_int;
let module = eponymous_only_module::<DummyTab>(); let module = eponymous_only_module::<DummyTab>();
@ -19,9 +20,9 @@ fn test_dummy_module() {
base: sqlite3_vtab, base: sqlite3_vtab,
} }
unsafe impl VTab for DummyTab { unsafe impl<'vtab> VTab<'vtab> for DummyTab {
type Aux = (); type Aux = ();
type Cursor = DummyTabCursor; type Cursor = DummyTabCursor<'vtab>;
fn connect( fn connect(
_: &mut VTabConnection, _: &mut VTabConnection,
@ -39,21 +40,22 @@ fn test_dummy_module() {
Ok(()) Ok(())
} }
fn open(&self) -> Result<DummyTabCursor> { fn open(&'vtab self) -> Result<DummyTabCursor<'vtab>> {
Ok(DummyTabCursor::default()) Ok(DummyTabCursor::default())
} }
} }
#[derive(Default)] #[derive(Default)]
#[repr(C)] #[repr(C)]
struct DummyTabCursor { struct DummyTabCursor<'vtab> {
/// Base class. Must be first /// Base class. Must be first
base: sqlite3_vtab_cursor, base: sqlite3_vtab_cursor,
/// The rowid /// The rowid
row_id: i64, row_id: i64,
phantom: PhantomData<&'vtab DummyTab>,
} }
unsafe impl VTabCursor for DummyTabCursor { unsafe impl VTabCursor for DummyTabCursor<'_> {
fn filter( fn filter(
&mut self, &mut self,
_idx_num: c_int, _idx_num: c_int,