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