From afeb5d4d4757f5c0d9affc10d3848e8f4178c654 Mon Sep 17 00:00:00 2001 From: gwenn Date: Wed, 20 Jun 2018 20:01:38 +0200 Subject: [PATCH] Introduce Module trait to make create_module type safe --- src/vtab/array.rs | 38 +++++++++++++++++------- src/vtab/csvtab.rs | 73 +++++++++++++++++++++++++++------------------- src/vtab/mod.rs | 61 +++++++++++++++++++++++++------------- src/vtab/series.rs | 44 +++++++++++++++++++--------- 4 files changed, 141 insertions(+), 75 deletions(-) diff --git a/src/vtab/array.rs b/src/vtab/array.rs index 3eadcd0..9cb6a6f 100644 --- a/src/vtab/array.rs +++ b/src/vtab/array.rs @@ -6,7 +6,7 @@ use std::rc::Rc; use ffi; use types::{ToSql, ToSqlOutput, Value}; -use vtab::{self, declare_vtab, Context, IndexInfo, VTab, VTabCursor, Values}; +use vtab::{self, declare_vtab, Context, IndexInfo, Module, VTab, VTabCursor, Values}; use {Connection, Error, Result}; // http://sqlite.org/bindptr.html @@ -28,11 +28,12 @@ impl ToSql for Array { /// Register the "rarray" module. pub fn load_module(conn: &Connection) -> Result<()> { let aux: Option<()> = None; - conn.create_module("rarray", &ARRAY_MODULE, aux) + conn.create_module("rarray", ArrayModule(&ARRAY_MODULE), aux) } eponymous_module!( ARRAY_MODULE, + ArrayModule, ArrayTab, (), ArrayTabCursor, @@ -50,6 +51,30 @@ eponymous_module!( array_rowid ); +#[repr(C)] +struct ArrayModule(&'static ffi::sqlite3_module); + +impl Module for ArrayModule { + type Aux = (); + type Table = ArrayTab; + + fn as_ptr(&self) -> *const ffi::sqlite3_module { + self.0 + } + + unsafe fn connect( + db: *mut ffi::sqlite3, + _aux: Option<&()>, + _args: &[&[u8]], + ) -> Result { + let vtab = ArrayTab { + base: Default::default(), + }; + try!(declare_vtab(db, "CREATE TABLE x(value,pointer hidden)")); + Ok(vtab) + } +} + // Column numbers // const CARRAY_COLUMN_VALUE : c_int = 0; const CARRAY_COLUMN_POINTER: c_int = 1; @@ -62,17 +87,8 @@ struct ArrayTab { } impl VTab for ArrayTab { - type Aux = (); type Cursor = ArrayTabCursor; - unsafe fn connect(db: *mut ffi::sqlite3, _aux: *mut (), _args: &[&[u8]]) -> Result { - let vtab = ArrayTab { - base: Default::default(), - }; - try!(declare_vtab(db, "CREATE TABLE x(value,pointer hidden)")); - Ok(vtab) - } - fn best_index(&self, info: &mut IndexInfo) -> Result<()> { // Index of the pointer= constraint let mut ptr_idx = None; diff --git a/src/vtab/csvtab.rs b/src/vtab/csvtab.rs index f1aa5b7..6d49be3 100644 --- a/src/vtab/csvtab.rs +++ b/src/vtab/csvtab.rs @@ -10,7 +10,7 @@ use std::str; use ffi; use types::Null; use vtab::{ - declare_vtab, dequote, escape_double_quote, parse_boolean, Context, IndexInfo, VTab, + declare_vtab, dequote, escape_double_quote, parse_boolean, Context, IndexInfo, Module, VTab, VTabCursor, Values, }; use {Connection, Error, Result}; @@ -28,11 +28,12 @@ use {Connection, Error, Result}; /// ``` pub fn load_module(conn: &Connection) -> Result<()> { let aux: Option<()> = None; - conn.create_module("csv", &CSV_MODULE, aux) + conn.create_module("csv", CSVModule(&CSV_MODULE), aux) } init_module!( CSV_MODULE, + CSVModule, CSVTab, (), CSVTabCursor, @@ -50,29 +51,9 @@ init_module!( csv_rowid ); -/// An instance of the CSV virtual table -#[repr(C)] -struct CSVTab { - /// Base class. Must be first - base: ffi::sqlite3_vtab, - /// Name of the CSV file - filename: String, - has_headers: bool, - delimiter: u8, - quote: u8, - /// Offset to start of data - offset_first_row: csv::Position, -} - -impl CSVTab { - fn reader(&self) -> result::Result, csv::Error> { - csv::ReaderBuilder::new() - .has_headers(self.has_headers) - .delimiter(self.delimiter) - .quote(self.quote) - .from_path(&self.filename) - } +struct CSVModule(&'static ffi::sqlite3_module); +impl CSVModule { fn parameter(c_slice: &[u8]) -> Result<(&str, &str)> { let arg = try!(str::from_utf8(c_slice)).trim(); let mut split = arg.split('='); @@ -95,11 +76,15 @@ impl CSVTab { } } -impl VTab for CSVTab { +impl Module for CSVModule { type Aux = (); - type Cursor = CSVTabCursor; + type Table = CSVTab; - unsafe fn connect(db: *mut ffi::sqlite3, _aux: *mut (), args: &[&[u8]]) -> Result { + fn as_ptr(&self) -> *const ffi::sqlite3_module { + self.0 + } + + unsafe fn connect(db: *mut ffi::sqlite3, _aux: Option<&()>, args: &[&[u8]]) -> Result { if args.len() < 4 { return Err(Error::ModuleError("no CSV file specified".to_owned())); } @@ -117,7 +102,7 @@ impl VTab for CSVTab { let args = &args[3..]; for c_slice in args { - let (param, value) = try!(CSVTab::parameter(c_slice)); + let (param, value) = try!(CSVModule::parameter(c_slice)); match param { "filename" => { if !Path::new(value).exists() { @@ -161,7 +146,7 @@ impl VTab for CSVTab { } } "delimiter" => { - if let Some(b) = CSVTab::parse_byte(value) { + if let Some(b) = CSVModule::parse_byte(value) { vtab.delimiter = b; } else { return Err(Error::ModuleError(format!( @@ -171,7 +156,7 @@ impl VTab for CSVTab { } } "quote" => { - if let Some(b) = CSVTab::parse_byte(value) { + if let Some(b) = CSVModule::parse_byte(value) { if b == b'0' { vtab.quote = 0; } else { @@ -248,6 +233,34 @@ impl VTab for CSVTab { try!(declare_vtab(db, &schema.unwrap())); Ok(vtab) } +} + +/// An instance of the CSV virtual table +#[repr(C)] +struct CSVTab { + /// Base class. Must be first + base: ffi::sqlite3_vtab, + /// Name of the CSV file + filename: String, + has_headers: bool, + delimiter: u8, + quote: u8, + /// Offset to start of data + offset_first_row: csv::Position, +} + +impl CSVTab { + fn reader(&self) -> result::Result, csv::Error> { + csv::ReaderBuilder::new() + .has_headers(self.has_headers) + .delimiter(self.delimiter) + .quote(self.quote) + .from_path(&self.filename) + } +} + +impl VTab for CSVTab { + type Cursor = CSVTabCursor; // Only a forward full table scan is supported. fn best_index(&self, info: &mut IndexInfo) -> Result<()> { diff --git a/src/vtab/mod.rs b/src/vtab/mod.rs index 2c7d4fb..bde9252 100644 --- a/src/vtab/mod.rs +++ b/src/vtab/mod.rs @@ -40,20 +40,36 @@ use {str_to_cstring, Connection, Error, InnerConnection, Result}; // \-> if not eof { cursor.column or xrowid } else { cursor.xclose } // -/// Virtual table instance trait. -pub trait VTab: Sized { +/// Module instance trait +pub trait Module { type Aux; - type Cursor: VTabCursor; + type Table: VTab; + + fn as_ptr(&self) -> *const ffi::sqlite3_module; /// 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 database connection that is executing /// the CREATE VIRTUAL TABLE statement. - unsafe fn create(db: *mut ffi::sqlite3, aux: *mut Self::Aux, args: &[&[u8]]) -> Result { + unsafe fn create( + db: *mut ffi::sqlite3, + aux: Option<&Self::Aux>, + args: &[&[u8]], + ) -> Result { Self::connect(db, aux, args) } /// Similar to `create`. The difference is that `connect` is called to establish a new connection /// to an _existing_ virtual table whereas `create` is called to create a new virtual table from scratch. - unsafe fn connect(db: *mut ffi::sqlite3, aux: *mut Self::Aux, args: &[&[u8]]) -> Result; + unsafe fn connect( + db: *mut ffi::sqlite3, + aux: Option<&Self::Aux>, + args: &[&[u8]], + ) -> Result; +} + +/// Virtual table instance trait. +pub trait VTab: Sized { + type Cursor: VTabCursor; + /// Determine the best way to access the virtual table. fn best_index(&self, info: &mut IndexInfo) -> Result<()>; /// Create a new cursor used for accessing a virtual table. @@ -291,34 +307,34 @@ impl<'a> Iterator for ValueIter<'a> { impl Connection { /// Register a virtual table implementation. - pub fn create_module( + pub fn create_module( &self, module_name: &str, - module: *const ffi::sqlite3_module, - aux: Option, + module: M, + aux: Option, ) -> Result<()> { self.db.borrow_mut().create_module(module_name, module, aux) } } impl InnerConnection { - fn create_module( + fn create_module( &mut self, module_name: &str, - module: *const ffi::sqlite3_module, - aux: Option, + module: M, + aux: Option, ) -> Result<()> { let c_name = try!(str_to_cstring(module_name)); let r = match aux { Some(aux) => { - let boxed_aux: *mut A = Box::into_raw(Box::new(aux)); + let boxed_aux: *mut M::Aux = Box::into_raw(Box::new(aux)); unsafe { ffi::sqlite3_create_module_v2( self.db(), c_name.as_ptr(), - module, + module.as_ptr(), boxed_aux as *mut c_void, - Some(free_boxed_value::), + Some(free_boxed_value::), ) } } @@ -326,7 +342,7 @@ impl InnerConnection { ffi::sqlite3_create_module_v2( self.db(), c_name.as_ptr(), - module, + module.as_ptr(), ptr::null_mut(), None, ) @@ -401,6 +417,7 @@ unsafe extern "C" fn free_boxed_value(p: *mut c_void) { macro_rules! init_module { ( $module_name:ident, + $module:ident, $vtab:ident, $aux:ty, $cursor:ty, @@ -445,8 +462,9 @@ macro_rules! init_module { // 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. - create_or_connect!($vtab, $aux, $create, create); + create_or_connect!($module, $vtab, $aux, $create, create); common_decl!( + $module, $vtab, $aux, $cursor, @@ -469,6 +487,7 @@ macro_rules! init_module { macro_rules! eponymous_module { ( $module_name:ident, + $module:ident, $vtab:ident, $aux:ty, $cursor:ty, @@ -513,6 +532,7 @@ macro_rules! eponymous_module { }; common_decl!( + $module, $vtab, $aux, $cursor, @@ -532,7 +552,7 @@ macro_rules! eponymous_module { } // eponymous_module macro end macro_rules! create_or_connect { - ($vtab:ident, $aux:ty, $create_or_connect:ident, $vtab_func:ident) => { + ($module:ident, $vtab:ident, $aux:ty, $create_or_connect:ident, $module_func:ident) => { unsafe extern "C" fn $create_or_connect( db: *mut ffi::sqlite3, aux: *mut c_void, @@ -550,9 +570,9 @@ macro_rules! create_or_connect { let args = slice::from_raw_parts(argv, argc as usize); let vec = args .iter() - .map(|&cs| CStr::from_ptr(cs).to_bytes()) + .map(|&cs| CStr::from_ptr(cs).to_bytes()) // FIXME .to_str() -> Result<&str, Utf8Error> .collect::>(); - match $vtab::$vtab_func(db, aux, &vec[..]) { + match $module::$module_func(db, aux.as_ref(), &vec[..]) { Ok(vtab) => { let boxed_vtab: *mut $vtab = Box::into_raw(Box::new(vtab)); *pp_vtab = boxed_vtab as *mut ffi::sqlite3_vtab; @@ -575,6 +595,7 @@ macro_rules! create_or_connect { macro_rules! common_decl { ( + $module:ident, $vtab:ident, $aux:ty, $cursor:ty, @@ -590,7 +611,7 @@ macro_rules! common_decl { $column:ident, $rowid:ident ) => { - create_or_connect!($vtab, $aux, $connect, connect); + create_or_connect!($module, $vtab, $aux, $connect, connect); unsafe extern "C" fn $best_index( vtab: *mut ffi::sqlite3_vtab, info: *mut ffi::sqlite3_index_info, diff --git a/src/vtab/series.rs b/src/vtab/series.rs index bcf6d16..68562eb 100644 --- a/src/vtab/series.rs +++ b/src/vtab/series.rs @@ -5,17 +5,18 @@ use std::os::raw::{c_char, c_int, c_void}; use ffi; use types::Type; -use vtab::{self, declare_vtab, Context, IndexInfo, VTab, VTabCursor, Values}; +use vtab::{self, declare_vtab, Context, IndexInfo, Module, VTab, VTabCursor, Values}; use {Connection, Error, Result}; /// Register the "generate_series" module. pub fn load_module(conn: &Connection) -> Result<()> { let aux: Option<()> = None; - conn.create_module("generate_series", &SERIES_MODULE, aux) + conn.create_module("generate_series", Series(&SERIES_MODULE), aux) } eponymous_module!( SERIES_MODULE, + Series, SeriesTab, (), SeriesTabCursor, @@ -33,6 +34,33 @@ eponymous_module!( series_rowid ); +#[repr(C)] +struct Series(&'static ffi::sqlite3_module); + +impl Module for Series { + type Aux = (); + type Table = SeriesTab; + + fn as_ptr(&self) -> *const ffi::sqlite3_module { + self.0 + } + + unsafe fn connect( + db: *mut ffi::sqlite3, + _aux: Option<&()>, + _args: &[&[u8]], + ) -> Result { + let vtab = SeriesTab { + base: Default::default(), + }; + try!(declare_vtab( + db, + "CREATE TABLE x(value,start hidden,stop hidden,step hidden)" + )); + Ok(vtab) + } +} + // Column numbers // const SERIES_COLUMN_VALUE : c_int = 0; const SERIES_COLUMN_START: c_int = 1; @@ -63,20 +91,8 @@ struct SeriesTab { } impl VTab for SeriesTab { - type Aux = (); type Cursor = SeriesTabCursor; - unsafe fn connect(db: *mut ffi::sqlite3, _aux: *mut (), _args: &[&[u8]]) -> Result { - let vtab = SeriesTab { - base: Default::default(), - }; - try!(declare_vtab( - db, - "CREATE TABLE x(value,start hidden,stop hidden,step hidden)" - )); - Ok(vtab) - } - fn best_index(&self, info: &mut IndexInfo) -> Result<()> { // The query plan bitmask let mut idx_num: QueryPlanFlags = QueryPlanFlags::empty();