mirror of
https://github.com/isar/rusqlite.git
synced 2024-11-26 11:31:37 +08:00
Rustfmt
This commit is contained in:
parent
8e6ea05efa
commit
2e2b5c41f4
@ -7,10 +7,13 @@ use std::path::Path;
|
||||
use std::result;
|
||||
use std::str;
|
||||
|
||||
use {Connection, Error, Result};
|
||||
use ffi;
|
||||
use types::Null;
|
||||
use vtab::{declare_vtab, dequote, escape_double_quote, parse_boolean, Context, IndexInfo, Values, VTab, VTabCursor};
|
||||
use vtab::{
|
||||
declare_vtab, dequote, escape_double_quote, parse_boolean, Context, IndexInfo, VTab,
|
||||
VTabCursor, Values,
|
||||
};
|
||||
use {Connection, Error, Result};
|
||||
|
||||
/// Register the "csv" module.
|
||||
/// ```sql
|
||||
@ -28,7 +31,8 @@ pub fn load_module(conn: &Connection) -> Result<()> {
|
||||
conn.create_module("csv", &CSV_MODULE, aux)
|
||||
}
|
||||
|
||||
init_module!(CSV_MODULE,
|
||||
init_module!(
|
||||
CSV_MODULE,
|
||||
CSVTab,
|
||||
CSVTabCursor,
|
||||
csv_create,
|
||||
@ -42,7 +46,8 @@ init_module!(CSV_MODULE,
|
||||
csv_next,
|
||||
csv_eof,
|
||||
csv_column,
|
||||
csv_rowid);
|
||||
csv_rowid
|
||||
);
|
||||
|
||||
/// An instance of the CSV virtual table
|
||||
#[repr(C)]
|
||||
@ -61,7 +66,8 @@ struct CSVTab {
|
||||
impl CSVTab {
|
||||
fn reader(&self) -> result::Result<csv::Reader<File>, csv::Error> {
|
||||
csv::Reader::from_file(&self.filename).map(|reader| {
|
||||
reader.has_headers(self.has_headers)
|
||||
reader
|
||||
.has_headers(self.has_headers)
|
||||
.delimiter(self.delimiter)
|
||||
.quote(self.quote)
|
||||
})
|
||||
@ -114,39 +120,55 @@ impl VTab for CSVTab {
|
||||
match param {
|
||||
"filename" => {
|
||||
if !Path::new(value).exists() {
|
||||
return Err(Error::ModuleError(format!("file '{}' does not exist", value)));
|
||||
return Err(Error::ModuleError(format!(
|
||||
"file '{}' does not exist",
|
||||
value
|
||||
)));
|
||||
}
|
||||
vtab.filename = value.to_owned();
|
||||
},
|
||||
}
|
||||
"schema" => {
|
||||
schema = Some(value.to_owned());
|
||||
},
|
||||
}
|
||||
"columns" => {
|
||||
if let Ok(n) = value.parse::<u16>() {
|
||||
if n_col.is_some() {
|
||||
return Err(Error::ModuleError("more than one 'columns' parameter".to_owned()));
|
||||
return Err(Error::ModuleError(
|
||||
"more than one 'columns' parameter".to_owned(),
|
||||
));
|
||||
} else if n == 0 {
|
||||
return Err(Error::ModuleError("must have at least one column".to_owned()));
|
||||
return Err(Error::ModuleError(
|
||||
"must have at least one column".to_owned(),
|
||||
));
|
||||
}
|
||||
n_col = Some(n);
|
||||
} else {
|
||||
return Err(Error::ModuleError(format!("unrecognized argument to 'columns': {}", value)));
|
||||
return Err(Error::ModuleError(format!(
|
||||
"unrecognized argument to 'columns': {}",
|
||||
value
|
||||
)));
|
||||
}
|
||||
}
|
||||
},
|
||||
"header" => {
|
||||
if let Some(b) = parse_boolean(value) {
|
||||
vtab.has_headers = b;
|
||||
} else {
|
||||
return Err(Error::ModuleError(format!("unrecognized argument to 'header': {}", value)));
|
||||
return Err(Error::ModuleError(format!(
|
||||
"unrecognized argument to 'header': {}",
|
||||
value
|
||||
)));
|
||||
}
|
||||
}
|
||||
},
|
||||
"delimiter" => {
|
||||
if let Some(b) = CSVTab::parse_byte(value) {
|
||||
vtab.delimiter = b;
|
||||
} else {
|
||||
return Err(Error::ModuleError(format!("unrecognized argument to 'delimiter': {}", value)));
|
||||
return Err(Error::ModuleError(format!(
|
||||
"unrecognized argument to 'delimiter': {}",
|
||||
value
|
||||
)));
|
||||
}
|
||||
}
|
||||
},
|
||||
"quote" => {
|
||||
if let Some(b) = CSVTab::parse_byte(value) {
|
||||
if b == b'0' {
|
||||
@ -155,12 +177,18 @@ impl VTab for CSVTab {
|
||||
vtab.quote = b;
|
||||
}
|
||||
} else {
|
||||
return Err(Error::ModuleError(format!("unrecognized argument to 'quote': {}", value)));
|
||||
return Err(Error::ModuleError(format!(
|
||||
"unrecognized argument to 'quote': {}",
|
||||
value
|
||||
)));
|
||||
}
|
||||
}
|
||||
},
|
||||
_ => {
|
||||
return Err(Error::ModuleError(format!("unrecognized parameter '{}'", param)));
|
||||
},
|
||||
return Err(Error::ModuleError(format!(
|
||||
"unrecognized parameter '{}'",
|
||||
param
|
||||
)));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -176,14 +204,17 @@ impl VTab for CSVTab {
|
||||
vtab.offset_first_row = reader.byte_offset();
|
||||
// headers ignored if cols is not empty
|
||||
if n_col.is_none() && schema.is_none() {
|
||||
cols = headers.into_iter().map(|header| escape_double_quote(&header).into_owned()).collect();
|
||||
cols = headers
|
||||
.into_iter()
|
||||
.map(|header| escape_double_quote(&header).into_owned())
|
||||
.collect();
|
||||
}
|
||||
} else {
|
||||
let mut count = 0;
|
||||
while let Some(col) = reader.next_bytes().into_iter_result() {
|
||||
try!(col);
|
||||
cols.push(format!("c{}", count));
|
||||
count+=1;
|
||||
count += 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -252,16 +283,12 @@ impl VTabCursor for CSVTabCursor {
|
||||
type Table = CSVTab;
|
||||
|
||||
fn vtab(&self) -> &CSVTab {
|
||||
unsafe { & *(self.base.pVtab as *const CSVTab) }
|
||||
unsafe { &*(self.base.pVtab as *const CSVTab) }
|
||||
}
|
||||
|
||||
// Only a full table scan is supported. So `filter` simply rewinds to
|
||||
// the beginning.
|
||||
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 offset_first_row = self.vtab().offset_first_row;
|
||||
try!(self.reader.seek(offset_first_row));
|
||||
@ -290,7 +317,10 @@ impl VTabCursor for CSVTabCursor {
|
||||
}
|
||||
fn column(&self, ctx: &mut Context, col: c_int) -> Result<()> {
|
||||
if col < 0 || col as usize >= self.cols.len() {
|
||||
return Err(Error::ModuleError(format!("column index out of bounds: {}", col)));
|
||||
return Err(Error::ModuleError(format!(
|
||||
"column index out of bounds: {}",
|
||||
col
|
||||
)));
|
||||
}
|
||||
if self.cols.is_empty() {
|
||||
ctx.set_result(&Null);
|
||||
@ -314,14 +344,15 @@ impl From<csv::Error> for Error {
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use {Connection, Result};
|
||||
use vtab::csvtab;
|
||||
use {Connection, Result};
|
||||
|
||||
#[test]
|
||||
fn test_csv_module() {
|
||||
let db = Connection::open_in_memory().unwrap();
|
||||
csvtab::load_module(&db).unwrap();
|
||||
db.execute_batch("CREATE VIRTUAL TABLE vtab USING csv(filename='test.csv', header=yes)").unwrap();
|
||||
db.execute_batch("CREATE VIRTUAL TABLE vtab USING csv(filename='test.csv', header=yes)")
|
||||
.unwrap();
|
||||
|
||||
{
|
||||
let mut s = db.prepare("SELECT rowid, * FROM vtab").unwrap();
|
||||
@ -330,8 +361,9 @@ mod test {
|
||||
assert_eq!(vec!["rowid", "colA", "colB", "colC"], headers);
|
||||
}
|
||||
|
||||
let ids: Result<Vec<i32>> =
|
||||
s.query_map(&[], |row| row.get::<i32, i32>(0)).unwrap().collect();
|
||||
let ids: Result<Vec<i32>> = s.query_map(&[], |row| row.get::<i32, i32>(0))
|
||||
.unwrap()
|
||||
.collect();
|
||||
let sum = ids.unwrap().iter().fold(0, |acc, &id| acc + id);
|
||||
assert_eq!(sum, 15);
|
||||
}
|
||||
@ -342,13 +374,14 @@ mod test {
|
||||
fn test_csv_cursor() {
|
||||
let db = Connection::open_in_memory().unwrap();
|
||||
csvtab::load_module(&db).unwrap();
|
||||
db.execute_batch("CREATE VIRTUAL TABLE vtab USING csv(filename='test.csv', header=yes)").unwrap();
|
||||
db.execute_batch("CREATE VIRTUAL TABLE vtab USING csv(filename='test.csv', header=yes)")
|
||||
.unwrap();
|
||||
|
||||
{
|
||||
let mut s =
|
||||
db.prepare("SELECT v1.rowid, v1.* FROM vtab v1 NATURAL JOIN vtab v2 WHERE \
|
||||
v1.rowid < v2.rowid")
|
||||
.unwrap();
|
||||
let mut s = db.prepare(
|
||||
"SELECT v1.rowid, v1.* FROM vtab v1 NATURAL JOIN vtab v2 WHERE \
|
||||
v1.rowid < v2.rowid",
|
||||
).unwrap();
|
||||
|
||||
let mut rows = s.query(&[]).unwrap();
|
||||
let row = rows.next().unwrap().unwrap();
|
||||
|
@ -5,9 +5,9 @@ use std::default::Default;
|
||||
use std::os::raw::{c_char, c_int, c_void};
|
||||
use std::rc::Rc;
|
||||
|
||||
use {Connection, Error, Result};
|
||||
use ffi;
|
||||
use vtab::{declare_vtab, escape_double_quote, Context, IndexInfo, Values, VTab, VTabCursor};
|
||||
use vtab::{declare_vtab, escape_double_quote, Context, IndexInfo, VTab, VTabCursor, Values};
|
||||
use {Connection, Error, Result};
|
||||
|
||||
/// Create a specific instance of an intarray object.
|
||||
/// The new intarray object is returned.
|
||||
@ -17,8 +17,10 @@ use vtab::{declare_vtab, escape_double_quote, Context, IndexInfo, Values, VTab,
|
||||
pub fn create_int_array(conn: &Connection, name: &str) -> Result<Rc<RefCell<Vec<i64>>>> {
|
||||
let array = Rc::new(RefCell::new(Vec::new()));
|
||||
try!(conn.create_module(name, &INT_ARRAY_MODULE, Some(array.clone())));
|
||||
try!(conn.execute_batch(&format!("CREATE VIRTUAL TABLE temp.\"{0}\" USING \"{0}\"",
|
||||
escape_double_quote(name))));
|
||||
try!(conn.execute_batch(&format!(
|
||||
"CREATE VIRTUAL TABLE temp.\"{0}\" USING \"{0}\"",
|
||||
escape_double_quote(name)
|
||||
)));
|
||||
Ok(array)
|
||||
}
|
||||
|
||||
@ -28,15 +30,18 @@ pub fn create_int_array(conn: &Connection, name: &str) -> Result<Rc<RefCell<Vec<
|
||||
/// In fact, the intarray is not destroy until the connection is closed
|
||||
/// because there is no other way to destroy the associated module.
|
||||
pub fn drop_int_array(conn: &Connection, name: &str) -> Result<()> {
|
||||
conn.execute_batch(&format!("DROP TABLE temp.\"{0}\"", escape_double_quote(name)))
|
||||
conn.execute_batch(&format!(
|
||||
"DROP TABLE temp.\"{0}\"",
|
||||
escape_double_quote(name)
|
||||
))
|
||||
// http://www.mail-archive.com/sqlite-users%40mailinglists.sqlite.org/msg08423.html
|
||||
// "Once a virtual table module has been created, it cannot be modified or destroyed, except by closing the database connection."
|
||||
// let aux: Option<()> = None;
|
||||
// conn.create_module(name, ptr::null() as *const ffi::sqlite3_module, aux)
|
||||
|
||||
}
|
||||
|
||||
eponymous_module!(INT_ARRAY_MODULE,
|
||||
eponymous_module!(
|
||||
INT_ARRAY_MODULE,
|
||||
IntArrayVTab,
|
||||
IntArrayVTabCursor,
|
||||
Some(int_array_connect),
|
||||
@ -50,7 +55,8 @@ eponymous_module!(INT_ARRAY_MODULE,
|
||||
int_array_next,
|
||||
int_array_eof,
|
||||
int_array_column,
|
||||
int_array_rowid);
|
||||
int_array_rowid
|
||||
);
|
||||
|
||||
#[repr(C)]
|
||||
struct IntArrayVTab {
|
||||
@ -62,16 +68,20 @@ struct IntArrayVTab {
|
||||
impl VTab for IntArrayVTab {
|
||||
type Cursor = IntArrayVTabCursor;
|
||||
|
||||
unsafe fn connect(db: *mut ffi::sqlite3,
|
||||
unsafe fn connect(
|
||||
db: *mut ffi::sqlite3,
|
||||
aux: *mut c_void,
|
||||
_args: &[&[u8]])
|
||||
-> Result<IntArrayVTab> {
|
||||
_args: &[&[u8]],
|
||||
) -> Result<IntArrayVTab> {
|
||||
let array = aux as *const Rc<RefCell<Vec<i64>>>;
|
||||
let vtab = IntArrayVTab {
|
||||
base: Default::default(),
|
||||
array,
|
||||
};
|
||||
try!(declare_vtab(db, "CREATE TABLE x(value INTEGER PRIMARY KEY)"));
|
||||
try!(declare_vtab(
|
||||
db,
|
||||
"CREATE TABLE x(value INTEGER PRIMARY KEY)"
|
||||
));
|
||||
Ok(vtab)
|
||||
}
|
||||
|
||||
@ -106,13 +116,9 @@ impl VTabCursor for IntArrayVTabCursor {
|
||||
type Table = IntArrayVTab;
|
||||
|
||||
fn vtab(&self) -> &IntArrayVTab {
|
||||
unsafe { & *(self.base.pVtab as *const IntArrayVTab) }
|
||||
unsafe { &*(self.base.pVtab as *const IntArrayVTab) }
|
||||
}
|
||||
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<()> {
|
||||
self.i = 0;
|
||||
Ok(())
|
||||
}
|
||||
@ -142,8 +148,8 @@ impl VTabCursor for IntArrayVTabCursor {
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use Connection;
|
||||
use vtab::int_array;
|
||||
use Connection;
|
||||
|
||||
#[test]
|
||||
#[cfg_attr(rustfmt, rustfmt_skip)]
|
||||
|
293
src/vtab/mod.rs
293
src/vtab/mod.rs
@ -6,11 +6,11 @@ use std::os::raw::{c_char, c_int, c_void};
|
||||
use std::ptr;
|
||||
use std::slice;
|
||||
|
||||
use {Connection, Error, Result, InnerConnection, str_to_cstring};
|
||||
use error::error_from_sqlite_code;
|
||||
use ffi;
|
||||
use functions::{set_result, report_error};
|
||||
use functions::{report_error, set_result};
|
||||
use types::{FromSql, FromSqlError, ToSql, ValueRef};
|
||||
use {str_to_cstring, Connection, Error, InnerConnection, Result};
|
||||
|
||||
// let conn: Connection = ...;
|
||||
// let mod: Module = ...; // VTab builder
|
||||
@ -77,7 +77,9 @@ impl IndexInfo {
|
||||
pub fn constraints(&self) -> IndexConstraintIter {
|
||||
let constraints =
|
||||
unsafe { slice::from_raw_parts((*self.0).aConstraint, (*self.0).nConstraint as usize) };
|
||||
IndexConstraintIter { iter: constraints.iter() }
|
||||
IndexConstraintIter {
|
||||
iter: constraints.iter(),
|
||||
}
|
||||
}
|
||||
|
||||
/// Number of terms in the ORDER BY clause
|
||||
@ -241,7 +243,9 @@ impl<'a> Values<'a> {
|
||||
}
|
||||
|
||||
pub fn iter(&self) -> ValueIter {
|
||||
ValueIter { iter: self.args.iter() }
|
||||
ValueIter {
|
||||
iter: self.args.iter(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -262,7 +266,9 @@ impl<'a> Iterator for ValueIter<'a> {
|
||||
type Item = ValueRef<'a>;
|
||||
|
||||
fn next(&mut self) -> Option<ValueRef<'a>> {
|
||||
self.iter.next().map(|&raw| { unsafe { ValueRef::from_value(raw) } })
|
||||
self.iter
|
||||
.next()
|
||||
.map(|&raw| unsafe { ValueRef::from_value(raw) })
|
||||
}
|
||||
|
||||
fn size_hint(&self) -> (usize, Option<usize>) {
|
||||
@ -272,41 +278,45 @@ impl<'a> Iterator for ValueIter<'a> {
|
||||
|
||||
impl Connection {
|
||||
/// Register a virtual table implementation.
|
||||
pub fn create_module<A>(&self,
|
||||
pub fn create_module<A>(
|
||||
&self,
|
||||
module_name: &str,
|
||||
module: *const ffi::sqlite3_module,
|
||||
aux: Option<A>)
|
||||
-> Result<()> {
|
||||
self.db
|
||||
.borrow_mut()
|
||||
.create_module(module_name, module, aux)
|
||||
aux: Option<A>,
|
||||
) -> Result<()> {
|
||||
self.db.borrow_mut().create_module(module_name, module, aux)
|
||||
}
|
||||
}
|
||||
|
||||
impl InnerConnection {
|
||||
fn create_module<A>(&mut self,
|
||||
fn create_module<A>(
|
||||
&mut self,
|
||||
module_name: &str,
|
||||
module: *const ffi::sqlite3_module,
|
||||
aux: Option<A>)
|
||||
-> Result<()> {
|
||||
aux: Option<A>,
|
||||
) -> 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));
|
||||
unsafe {
|
||||
ffi::sqlite3_create_module_v2(self.db(),
|
||||
ffi::sqlite3_create_module_v2(
|
||||
self.db(),
|
||||
c_name.as_ptr(),
|
||||
module,
|
||||
boxed_aux as *mut c_void,
|
||||
Some(free_boxed_value::<A>))
|
||||
Some(free_boxed_value::<A>),
|
||||
)
|
||||
}
|
||||
}
|
||||
None => unsafe {
|
||||
ffi::sqlite3_create_module_v2(self.db(),
|
||||
ffi::sqlite3_create_module_v2(
|
||||
self.db(),
|
||||
c_name.as_ptr(),
|
||||
module,
|
||||
ptr::null_mut(),
|
||||
None)
|
||||
None,
|
||||
)
|
||||
},
|
||||
};
|
||||
self.decode_result(r)
|
||||
@ -339,11 +349,9 @@ pub fn dequote(s: &str) -> &str {
|
||||
return s;
|
||||
}
|
||||
match s.bytes().next() {
|
||||
Some(b) if b == b'"' || b == b'\'' => {
|
||||
match s.bytes().rev().next() {
|
||||
Some(e) if e == b => &s[1..s.len()-1],
|
||||
Some(b) if b == b'"' || b == b'\'' => match s.bytes().rev().next() {
|
||||
Some(e) if e == b => &s[1..s.len() - 1],
|
||||
_ => s,
|
||||
}
|
||||
},
|
||||
_ => s,
|
||||
}
|
||||
@ -354,9 +362,13 @@ pub fn dequote(s: &str) -> &str {
|
||||
/// 0 no false off
|
||||
/// ```
|
||||
pub fn parse_boolean(s: &str) -> Option<bool> {
|
||||
if s.eq_ignore_ascii_case("yes") || s.eq_ignore_ascii_case("on") || s.eq_ignore_ascii_case("true") || s.eq("1") {
|
||||
if s.eq_ignore_ascii_case("yes") || s.eq_ignore_ascii_case("on")
|
||||
|| s.eq_ignore_ascii_case("true") || s.eq("1")
|
||||
{
|
||||
Some(true)
|
||||
} else if s.eq_ignore_ascii_case("no") || s.eq_ignore_ascii_case("off") || s.eq_ignore_ascii_case("false") || s.eq("0") {
|
||||
} else if s.eq_ignore_ascii_case("no") || s.eq_ignore_ascii_case("off")
|
||||
|| s.eq_ignore_ascii_case("false") || s.eq("0")
|
||||
{
|
||||
Some(false)
|
||||
} else {
|
||||
None
|
||||
@ -370,14 +382,24 @@ unsafe extern "C" fn free_boxed_value<T>(p: *mut c_void) {
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! init_module {
|
||||
($module_name: ident, $vtab: ident, $cursor: ty,
|
||||
$create: ident, $connect: ident, $best_index: ident,
|
||||
$disconnect: ident, $destroy: ident,
|
||||
$open: ident, $close: ident,
|
||||
$filter: ident, $next: ident, $eof: ident,
|
||||
$column: ident, $rowid: ident) => {
|
||||
|
||||
static $module_name: ffi::sqlite3_module = ffi::sqlite3_module {
|
||||
(
|
||||
$module_name:ident,
|
||||
$vtab:ident,
|
||||
$cursor:ty,
|
||||
$create:ident,
|
||||
$connect:ident,
|
||||
$best_index:ident,
|
||||
$disconnect:ident,
|
||||
$destroy:ident,
|
||||
$open:ident,
|
||||
$close:ident,
|
||||
$filter:ident,
|
||||
$next:ident,
|
||||
$eof:ident,
|
||||
$column:ident,
|
||||
$rowid:ident
|
||||
) => {
|
||||
static $module_name: ffi::sqlite3_module = ffi::sqlite3_module {
|
||||
iVersion: 1,
|
||||
xCreate: Some($create),
|
||||
xConnect: Some($connect),
|
||||
@ -401,31 +423,49 @@ static $module_name: ffi::sqlite3_module = ffi::sqlite3_module {
|
||||
xSavepoint: None,
|
||||
xRelease: None,
|
||||
xRollbackTo: None,
|
||||
};
|
||||
};
|
||||
|
||||
// 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, $create, create);
|
||||
common_decl!($vtab, $cursor,
|
||||
$connect, $best_index,
|
||||
$disconnect, $destroy,
|
||||
$open, $close,
|
||||
$filter, $next, $eof,
|
||||
$column, $rowid
|
||||
);
|
||||
}
|
||||
// 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, $create, create);
|
||||
common_decl!(
|
||||
$vtab,
|
||||
$cursor,
|
||||
$connect,
|
||||
$best_index,
|
||||
$disconnect,
|
||||
$destroy,
|
||||
$open,
|
||||
$close,
|
||||
$filter,
|
||||
$next,
|
||||
$eof,
|
||||
$column,
|
||||
$rowid
|
||||
);
|
||||
};
|
||||
} // init_module macro end
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! eponymous_module {
|
||||
($module_name: ident, $vtab: ident, $cursor: ty,
|
||||
$create: expr, $connect: ident, $best_index: ident,
|
||||
$disconnect: ident, $destroy: expr,
|
||||
$open: ident, $close: ident,
|
||||
$filter: ident, $next: ident, $eof: ident,
|
||||
$column: ident, $rowid: ident) => {
|
||||
|
||||
static $module_name: ffi::sqlite3_module = ffi::sqlite3_module {
|
||||
(
|
||||
$module_name:ident,
|
||||
$vtab:ident,
|
||||
$cursor:ty,
|
||||
$create:expr,
|
||||
$connect:ident,
|
||||
$best_index:ident,
|
||||
$disconnect:ident,
|
||||
$destroy:expr,
|
||||
$open:ident,
|
||||
$close:ident,
|
||||
$filter:ident,
|
||||
$next:ident,
|
||||
$eof:ident,
|
||||
$column:ident,
|
||||
$rowid:ident
|
||||
) => {
|
||||
static $module_name: ffi::sqlite3_module = ffi::sqlite3_module {
|
||||
iVersion: 1,
|
||||
xCreate: $create, /* For eponymous-only virtual tables, the xCreate method is NULL */
|
||||
xConnect: Some($connect), /* A virtual table is eponymous if its xCreate method is
|
||||
@ -450,67 +490,86 @@ static $module_name: ffi::sqlite3_module = ffi::sqlite3_module {
|
||||
xSavepoint: None,
|
||||
xRelease: None,
|
||||
xRollbackTo: None,
|
||||
};
|
||||
};
|
||||
|
||||
common_decl!($vtab, $cursor,
|
||||
$connect, $best_index,
|
||||
$disconnect, $destroy,
|
||||
$open, $close,
|
||||
$filter, $next, $eof,
|
||||
$column, $rowid
|
||||
);
|
||||
}
|
||||
common_decl!(
|
||||
$vtab,
|
||||
$cursor,
|
||||
$connect,
|
||||
$best_index,
|
||||
$disconnect,
|
||||
$destroy,
|
||||
$open,
|
||||
$close,
|
||||
$filter,
|
||||
$next,
|
||||
$eof,
|
||||
$column,
|
||||
$rowid
|
||||
);
|
||||
};
|
||||
} // eponymous_module macro end
|
||||
|
||||
macro_rules! create_or_connect {
|
||||
($vtab: ident, $create_or_connect: ident, $vtab_func: ident) => {
|
||||
unsafe extern "C" fn $create_or_connect(db: *mut ffi::sqlite3,
|
||||
($vtab:ident, $create_or_connect:ident, $vtab_func:ident) => {
|
||||
unsafe extern "C" fn $create_or_connect(
|
||||
db: *mut ffi::sqlite3,
|
||||
aux: *mut c_void,
|
||||
argc: c_int,
|
||||
argv: *const *const c_char,
|
||||
pp_vtab: *mut *mut ffi::sqlite3_vtab,
|
||||
err_msg: *mut *mut c_char)
|
||||
-> c_int {
|
||||
err_msg: *mut *mut c_char,
|
||||
) -> c_int {
|
||||
use std::error::Error as StdError;
|
||||
use std::ffi::CStr;
|
||||
use std::slice;
|
||||
use vtab::mprintf;
|
||||
let args = slice::from_raw_parts(argv, argc as usize);
|
||||
let vec = args.iter().map(|&cs| {
|
||||
CStr::from_ptr(cs).to_bytes()
|
||||
}).collect::<Vec<_>>();
|
||||
let vec = args.iter()
|
||||
.map(|&cs| CStr::from_ptr(cs).to_bytes())
|
||||
.collect::<Vec<_>>();
|
||||
match $vtab::$vtab_func(db, aux, &vec[..]) {
|
||||
Ok(vtab) => {
|
||||
let boxed_vtab: *mut $vtab = Box::into_raw(Box::new(vtab));
|
||||
*pp_vtab = boxed_vtab as *mut ffi::sqlite3_vtab;
|
||||
ffi::SQLITE_OK
|
||||
},
|
||||
}
|
||||
Err(Error::SqliteFailure(err, s)) => {
|
||||
if let Some(s) = s {
|
||||
*err_msg = mprintf(&s);
|
||||
}
|
||||
err.extended_code
|
||||
},
|
||||
}
|
||||
Err(err) => {
|
||||
*err_msg = mprintf(err.description());
|
||||
ffi::SQLITE_ERROR
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
} // create_or_connect macro end
|
||||
|
||||
macro_rules! common_decl {
|
||||
($vtab: ident, $cursor: ty,
|
||||
$connect: ident, $best_index: ident,
|
||||
$disconnect: ident, $destroy: expr,
|
||||
$open: ident, $close: ident,
|
||||
$filter: ident, $next: ident, $eof: ident,
|
||||
$column: ident, $rowid: ident) => {
|
||||
create_or_connect!($vtab, $connect, connect);
|
||||
unsafe extern "C" fn $best_index(vtab: *mut ffi::sqlite3_vtab,
|
||||
info: *mut ffi::sqlite3_index_info)
|
||||
-> c_int {
|
||||
(
|
||||
$vtab:ident,
|
||||
$cursor:ty,
|
||||
$connect:ident,
|
||||
$best_index:ident,
|
||||
$disconnect:ident,
|
||||
$destroy:expr,
|
||||
$open:ident,
|
||||
$close:ident,
|
||||
$filter:ident,
|
||||
$next:ident,
|
||||
$eof:ident,
|
||||
$column:ident,
|
||||
$rowid:ident
|
||||
) => {
|
||||
create_or_connect!($vtab, $connect, connect);
|
||||
unsafe extern "C" fn $best_index(
|
||||
vtab: *mut ffi::sqlite3_vtab,
|
||||
info: *mut ffi::sqlite3_index_info,
|
||||
) -> c_int {
|
||||
use std::error::Error as StdError;
|
||||
use vtab::set_err_msg;
|
||||
let vt = vtab as *mut $vtab;
|
||||
@ -522,24 +581,23 @@ unsafe extern "C" fn $best_index(vtab: *mut ffi::sqlite3_vtab,
|
||||
set_err_msg(vtab, &err_msg);
|
||||
}
|
||||
err.extended_code
|
||||
},
|
||||
}
|
||||
Err(err) => {
|
||||
set_err_msg(vtab, err.description());
|
||||
ffi::SQLITE_ERROR
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
unsafe extern "C" fn $disconnect(vtab: *mut ffi::sqlite3_vtab) -> c_int {
|
||||
}
|
||||
unsafe extern "C" fn $disconnect(vtab: *mut ffi::sqlite3_vtab) -> c_int {
|
||||
let vtab = vtab as *mut $vtab;
|
||||
let _: Box<$vtab> = Box::from_raw(vtab);
|
||||
ffi::SQLITE_OK
|
||||
}
|
||||
}
|
||||
|
||||
unsafe extern "C" fn $open(vtab: *mut ffi::sqlite3_vtab,
|
||||
pp_cursor: *mut *mut ffi::sqlite3_vtab_cursor)
|
||||
-> c_int {
|
||||
unsafe extern "C" fn $open(
|
||||
vtab: *mut ffi::sqlite3_vtab,
|
||||
pp_cursor: *mut *mut ffi::sqlite3_vtab_cursor,
|
||||
) -> c_int {
|
||||
use std::error::Error as StdError;
|
||||
use vtab::set_err_msg;
|
||||
let vt = vtab as *mut $vtab;
|
||||
@ -548,31 +606,32 @@ unsafe extern "C" fn $open(vtab: *mut ffi::sqlite3_vtab,
|
||||
let boxed_cursor: *mut $cursor = Box::into_raw(Box::new(cursor));
|
||||
*pp_cursor = boxed_cursor as *mut ffi::sqlite3_vtab_cursor;
|
||||
ffi::SQLITE_OK
|
||||
},
|
||||
}
|
||||
Err(Error::SqliteFailure(err, s)) => {
|
||||
if let Some(err_msg) = s {
|
||||
set_err_msg(vtab, &err_msg);
|
||||
}
|
||||
err.extended_code
|
||||
},
|
||||
}
|
||||
Err(err) => {
|
||||
set_err_msg(vtab, err.description());
|
||||
ffi::SQLITE_ERROR
|
||||
}
|
||||
}
|
||||
}
|
||||
unsafe extern "C" fn $close(cursor: *mut ffi::sqlite3_vtab_cursor) -> c_int {
|
||||
}
|
||||
unsafe extern "C" fn $close(cursor: *mut ffi::sqlite3_vtab_cursor) -> c_int {
|
||||
let cr = cursor as *mut $cursor;
|
||||
let _: Box<$cursor> = Box::from_raw(cr);
|
||||
ffi::SQLITE_OK
|
||||
}
|
||||
}
|
||||
|
||||
unsafe extern "C" fn $filter(cursor: *mut ffi::sqlite3_vtab_cursor,
|
||||
unsafe extern "C" fn $filter(
|
||||
cursor: *mut ffi::sqlite3_vtab_cursor,
|
||||
idx_num: c_int,
|
||||
idx_str: *const c_char,
|
||||
argc: c_int,
|
||||
argv: *mut *mut ffi::sqlite3_value)
|
||||
-> c_int {
|
||||
argv: *mut *mut ffi::sqlite3_value,
|
||||
) -> c_int {
|
||||
use std::ffi::CStr;
|
||||
use std::slice;
|
||||
use std::str;
|
||||
@ -587,45 +646,45 @@ unsafe extern "C" fn $filter(cursor: *mut ffi::sqlite3_vtab_cursor,
|
||||
let values = Values { args: args };
|
||||
let cr = cursor as *mut $cursor;
|
||||
cursor_error(cursor, (*cr).filter(idx_num, idx_name, &values))
|
||||
}
|
||||
unsafe extern "C" fn $next(cursor: *mut ffi::sqlite3_vtab_cursor) -> c_int {
|
||||
}
|
||||
unsafe extern "C" fn $next(cursor: *mut ffi::sqlite3_vtab_cursor) -> c_int {
|
||||
use vtab::cursor_error;
|
||||
let cr = cursor as *mut $cursor;
|
||||
cursor_error(cursor, (*cr).next())
|
||||
}
|
||||
unsafe extern "C" fn $eof(cursor: *mut ffi::sqlite3_vtab_cursor) -> c_int {
|
||||
}
|
||||
unsafe extern "C" fn $eof(cursor: *mut ffi::sqlite3_vtab_cursor) -> c_int {
|
||||
let cr = cursor as *mut $cursor;
|
||||
(*cr).eof() as c_int
|
||||
}
|
||||
unsafe extern "C" fn $column(cursor: *mut ffi::sqlite3_vtab_cursor,
|
||||
}
|
||||
unsafe extern "C" fn $column(
|
||||
cursor: *mut ffi::sqlite3_vtab_cursor,
|
||||
ctx: *mut ffi::sqlite3_context,
|
||||
i: c_int)
|
||||
-> c_int {
|
||||
i: c_int,
|
||||
) -> c_int {
|
||||
use vtab::{result_error, Context};
|
||||
let cr = cursor as *mut $cursor;
|
||||
let mut ctxt = Context(ctx);
|
||||
result_error(ctx, (*cr).column(&mut ctxt, i))
|
||||
}
|
||||
unsafe extern "C" fn $rowid(cursor: *mut ffi::sqlite3_vtab_cursor,
|
||||
p_rowid: *mut ffi::sqlite3_int64)
|
||||
-> c_int {
|
||||
}
|
||||
unsafe extern "C" fn $rowid(
|
||||
cursor: *mut ffi::sqlite3_vtab_cursor,
|
||||
p_rowid: *mut ffi::sqlite3_int64,
|
||||
) -> c_int {
|
||||
use vtab::cursor_error;
|
||||
let cr = cursor as *mut $cursor;
|
||||
match (*cr).rowid() {
|
||||
Ok(rowid) => {
|
||||
*p_rowid = rowid;
|
||||
ffi::SQLITE_OK
|
||||
},
|
||||
err => cursor_error(cursor, err)
|
||||
}
|
||||
}
|
||||
err => cursor_error(cursor, err),
|
||||
}
|
||||
}
|
||||
};
|
||||
} // common_decl macro end
|
||||
|
||||
/// Virtual table cursors can set an error message by assigning a string to `zErrMsg`.
|
||||
pub unsafe fn cursor_error<T>(cursor: *mut ffi::sqlite3_vtab_cursor,
|
||||
result: Result<T>)
|
||||
-> c_int {
|
||||
pub unsafe fn cursor_error<T>(cursor: *mut ffi::sqlite3_vtab_cursor, result: Result<T>) -> c_int {
|
||||
use std::error::Error as StdError;
|
||||
match result {
|
||||
Ok(_) => ffi::SQLITE_OK,
|
||||
@ -691,9 +750,9 @@ pub fn mprintf(err_msg: &str) -> *mut c_char {
|
||||
unsafe { ffi::sqlite3_mprintf(c_format.as_ptr(), c_err.as_ptr()) }
|
||||
}
|
||||
|
||||
pub mod int_array;
|
||||
#[cfg(feature = "csvtab")]
|
||||
pub mod csvtab;
|
||||
pub mod int_array;
|
||||
#[cfg(feature = "bundled")]
|
||||
pub mod series;
|
||||
|
||||
|
@ -3,9 +3,9 @@
|
||||
use std::default::Default;
|
||||
use std::os::raw::{c_char, c_int, c_void};
|
||||
|
||||
use {Connection, Error, Result};
|
||||
use ffi;
|
||||
use vtab::{self, declare_vtab, Context, IndexInfo, Values, VTab, VTabCursor};
|
||||
use vtab::{self, declare_vtab, Context, IndexInfo, VTab, VTabCursor, Values};
|
||||
use {Connection, Error, Result};
|
||||
|
||||
/// Register the "generate_series" module.
|
||||
pub fn load_module(conn: &Connection) -> Result<()> {
|
||||
@ -13,7 +13,8 @@ pub fn load_module(conn: &Connection) -> Result<()> {
|
||||
conn.create_module("generate_series", &SERIES_MODULE, aux)
|
||||
}
|
||||
|
||||
eponymous_module!(SERIES_MODULE,
|
||||
eponymous_module!(
|
||||
SERIES_MODULE,
|
||||
SeriesTab,
|
||||
SeriesTabCursor,
|
||||
None,
|
||||
@ -27,7 +28,8 @@ eponymous_module!(SERIES_MODULE,
|
||||
series_next,
|
||||
series_eof,
|
||||
series_column,
|
||||
series_rowid);
|
||||
series_rowid
|
||||
);
|
||||
|
||||
// Column numbers
|
||||
// const SERIES_COLUMN_VALUE : c_int = 0;
|
||||
@ -51,7 +53,6 @@ bitflags! {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// An instance of the Series virtual table
|
||||
#[repr(C)]
|
||||
struct SeriesTab {
|
||||
@ -59,17 +60,21 @@ struct SeriesTab {
|
||||
base: ffi::sqlite3_vtab,
|
||||
}
|
||||
|
||||
|
||||
impl VTab for SeriesTab {
|
||||
type Cursor = SeriesTabCursor;
|
||||
|
||||
unsafe fn connect(db: *mut ffi::sqlite3,
|
||||
unsafe fn connect(
|
||||
db: *mut ffi::sqlite3,
|
||||
_aux: *mut c_void,
|
||||
_args: &[&[u8]])
|
||||
-> Result<SeriesTab> {
|
||||
let vtab = SeriesTab { base: Default::default() };
|
||||
try!(declare_vtab(db,
|
||||
"CREATE TABLE x(value,start hidden,stop hidden,step hidden)"));
|
||||
_args: &[&[u8]],
|
||||
) -> Result<SeriesTab> {
|
||||
let vtab = SeriesTab {
|
||||
base: Default::default(),
|
||||
};
|
||||
try!(declare_vtab(
|
||||
db,
|
||||
"CREATE TABLE x(value,start hidden,stop hidden,step hidden)"
|
||||
));
|
||||
Ok(vtab)
|
||||
}
|
||||
|
||||
@ -127,7 +132,13 @@ impl VTab for SeriesTab {
|
||||
}
|
||||
if idx_num.contains(QueryPlanFlags::BOTH) {
|
||||
// Both start= and stop= boundaries are available.
|
||||
info.set_estimated_cost((2 - if idx_num.contains(QueryPlanFlags::STEP) { 1 } else { 0 }) as f64);
|
||||
info.set_estimated_cost(
|
||||
(2 - if idx_num.contains(QueryPlanFlags::STEP) {
|
||||
1
|
||||
} else {
|
||||
0
|
||||
}) as f64,
|
||||
);
|
||||
info.set_estimated_rows(1000);
|
||||
if info.num_of_order_by() == 1 {
|
||||
if info.is_order_by_desc(0) {
|
||||
@ -177,13 +188,9 @@ impl VTabCursor for SeriesTabCursor {
|
||||
type Table = SeriesTab;
|
||||
|
||||
fn vtab(&self) -> &SeriesTab {
|
||||
unsafe { & *(self.base.pVtab as *const SeriesTab) }
|
||||
unsafe { &*(self.base.pVtab as *const SeriesTab) }
|
||||
}
|
||||
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 mut i = 0;
|
||||
if idx_num.contains(QueryPlanFlags::START) {
|
||||
@ -251,9 +258,9 @@ impl VTabCursor for SeriesTabCursor {
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use Connection;
|
||||
use vtab::series;
|
||||
use ffi;
|
||||
use vtab::series;
|
||||
use Connection;
|
||||
|
||||
#[test]
|
||||
fn test_series_module() {
|
||||
@ -267,9 +274,7 @@ mod test {
|
||||
|
||||
let mut s = db.prepare("SELECT * FROM generate_series(0,20,5)").unwrap();
|
||||
|
||||
|
||||
let series = s.query_map(&[], |row| row.get::<i32, i32>(0))
|
||||
.unwrap();
|
||||
let series = s.query_map(&[], |row| row.get::<i32, i32>(0)).unwrap();
|
||||
|
||||
let mut expected = 0;
|
||||
for value in series {
|
||||
|
Loading…
Reference in New Issue
Block a user