mirror of
https://github.com/isar/rusqlite.git
synced 2024-11-26 11:31:37 +08:00
Draft for carray module in Rust
Incomplete support for sqlite3_bind_pointer. Make Context::set_result return a Result. Add Values::get_array.
This commit is contained in:
parent
b89b574f81
commit
fa64a4d0bf
@ -31,6 +31,8 @@ sqlcipher = ["libsqlite3-sys/sqlcipher"]
|
|||||||
unlock_notify = ["libsqlite3-sys/unlock_notify"]
|
unlock_notify = ["libsqlite3-sys/unlock_notify"]
|
||||||
vtab = ["libsqlite3-sys/min_sqlite_version_3_7_7"]
|
vtab = ["libsqlite3-sys/min_sqlite_version_3_7_7"]
|
||||||
csvtab = ["csv", "vtab"]
|
csvtab = ["csv", "vtab"]
|
||||||
|
# pointer passing interfaces: 3.20.0
|
||||||
|
array = ["vtab"]
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
time = "0.1.0"
|
time = "0.1.0"
|
||||||
|
@ -1,15 +1,18 @@
|
|||||||
//! Code related to `sqlite3_context` common to `functions` and `vtab` modules.
|
//! Code related to `sqlite3_context` common to `functions` and `vtab` modules.
|
||||||
|
|
||||||
use std::error::Error as StdError;
|
|
||||||
use std::ffi::CStr;
|
use std::ffi::CStr;
|
||||||
use std::os::raw::{c_char, c_int, c_void};
|
use std::os::raw::{c_char, c_int, c_void};
|
||||||
|
#[cfg(feature = "array")]
|
||||||
|
use std::rc::Rc;
|
||||||
|
|
||||||
use ffi;
|
use ffi;
|
||||||
use ffi::sqlite3_context;
|
use ffi::sqlite3_context;
|
||||||
use ffi::sqlite3_value;
|
use ffi::sqlite3_value;
|
||||||
|
|
||||||
|
use str_to_cstring;
|
||||||
use types::{ToSqlOutput, ValueRef};
|
use types::{ToSqlOutput, ValueRef};
|
||||||
use {str_to_cstring, Error};
|
#[cfg(feature = "array")]
|
||||||
|
use vtab::array::{free_array, ARRAY_TYPE};
|
||||||
|
|
||||||
impl<'a> ValueRef<'a> {
|
impl<'a> ValueRef<'a> {
|
||||||
pub unsafe fn from_value(value: *mut sqlite3_value) -> ValueRef<'a> {
|
pub unsafe fn from_value(value: *mut sqlite3_value) -> ValueRef<'a> {
|
||||||
@ -28,7 +31,8 @@ impl<'a> ValueRef<'a> {
|
|||||||
let s = CStr::from_ptr(text as *const c_char);
|
let s = CStr::from_ptr(text as *const c_char);
|
||||||
|
|
||||||
// sqlite3_value_text returns UTF8 data, so our unwrap here should be fine.
|
// sqlite3_value_text returns UTF8 data, so our unwrap here should be fine.
|
||||||
let s = s.to_str()
|
let s = s
|
||||||
|
.to_str()
|
||||||
.expect("sqlite3_value_text returned invalid UTF-8");
|
.expect("sqlite3_value_text returned invalid UTF-8");
|
||||||
ValueRef::Text(s)
|
ValueRef::Text(s)
|
||||||
}
|
}
|
||||||
@ -68,6 +72,15 @@ pub unsafe fn set_result<'a>(ctx: *mut sqlite3_context, result: &ToSqlOutput<'a>
|
|||||||
ToSqlOutput::ZeroBlob(len) => {
|
ToSqlOutput::ZeroBlob(len) => {
|
||||||
return ffi::sqlite3_result_zeroblob(ctx, len);
|
return ffi::sqlite3_result_zeroblob(ctx, len);
|
||||||
}
|
}
|
||||||
|
#[cfg(feature = "array")]
|
||||||
|
ToSqlOutput::Array(ref a) => {
|
||||||
|
return ffi::sqlite3_result_pointer(
|
||||||
|
ctx,
|
||||||
|
Rc::into_raw(a.clone()) as *mut c_void,
|
||||||
|
ARRAY_TYPE,
|
||||||
|
Some(free_array),
|
||||||
|
);
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
match value {
|
match value {
|
||||||
@ -109,33 +122,3 @@ pub unsafe fn set_result<'a>(ctx: *mut sqlite3_context, result: &ToSqlOutput<'a>
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub unsafe fn report_error(ctx: *mut sqlite3_context, err: &Error) {
|
|
||||||
// Extended constraint error codes were added in SQLite 3.7.16. We don't have an explicit
|
|
||||||
// feature check for that, and this doesn't really warrant one. We'll use the extended code
|
|
||||||
// if we're on the bundled version (since it's at least 3.17.0) and the normal constraint
|
|
||||||
// error code if not.
|
|
||||||
#[cfg(feature = "bundled")]
|
|
||||||
fn constraint_error_code() -> i32 {
|
|
||||||
ffi::SQLITE_CONSTRAINT_FUNCTION
|
|
||||||
}
|
|
||||||
#[cfg(not(feature = "bundled"))]
|
|
||||||
fn constraint_error_code() -> i32 {
|
|
||||||
ffi::SQLITE_CONSTRAINT
|
|
||||||
}
|
|
||||||
|
|
||||||
match *err {
|
|
||||||
Error::SqliteFailure(ref err, ref s) => {
|
|
||||||
ffi::sqlite3_result_error_code(ctx, err.extended_code);
|
|
||||||
if let Some(Ok(cstr)) = s.as_ref().map(|s| str_to_cstring(s)) {
|
|
||||||
ffi::sqlite3_result_error(ctx, cstr.as_ptr(), -1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
_ => {
|
|
||||||
ffi::sqlite3_result_error_code(ctx, constraint_error_code());
|
|
||||||
if let Ok(cstr) = str_to_cstring(err.description()) {
|
|
||||||
ffi::sqlite3_result_error(ctx, cstr.as_ptr(), -1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
@ -49,6 +49,7 @@
|
|||||||
//! assert!(is_match);
|
//! assert!(is_match);
|
||||||
//! }
|
//! }
|
||||||
//! ```
|
//! ```
|
||||||
|
use std::error::Error as StdError;
|
||||||
use std::ptr;
|
use std::ptr;
|
||||||
use std::slice;
|
use std::slice;
|
||||||
use std::os::raw::{c_int, c_void};
|
use std::os::raw::{c_int, c_void};
|
||||||
@ -57,11 +58,41 @@ use ffi;
|
|||||||
use ffi::sqlite3_context;
|
use ffi::sqlite3_context;
|
||||||
use ffi::sqlite3_value;
|
use ffi::sqlite3_value;
|
||||||
|
|
||||||
use context::{report_error, set_result};
|
use context::{set_result};
|
||||||
use types::{ToSql, FromSql, FromSqlError, ValueRef};
|
use types::{ToSql, FromSql, FromSqlError, ValueRef};
|
||||||
|
|
||||||
use {Result, Error, Connection, str_to_cstring, InnerConnection};
|
use {Result, Error, Connection, str_to_cstring, InnerConnection};
|
||||||
|
|
||||||
|
unsafe fn report_error(ctx: *mut sqlite3_context, err: &Error) {
|
||||||
|
// Extended constraint error codes were added in SQLite 3.7.16. We don't have an explicit
|
||||||
|
// feature check for that, and this doesn't really warrant one. We'll use the extended code
|
||||||
|
// if we're on the bundled version (since it's at least 3.17.0) and the normal constraint
|
||||||
|
// error code if not.
|
||||||
|
#[cfg(feature = "bundled")]
|
||||||
|
fn constraint_error_code() -> i32 {
|
||||||
|
ffi::SQLITE_CONSTRAINT_FUNCTION
|
||||||
|
}
|
||||||
|
#[cfg(not(feature = "bundled"))]
|
||||||
|
fn constraint_error_code() -> i32 {
|
||||||
|
ffi::SQLITE_CONSTRAINT
|
||||||
|
}
|
||||||
|
|
||||||
|
match *err {
|
||||||
|
Error::SqliteFailure(ref err, ref s) => {
|
||||||
|
ffi::sqlite3_result_error_code(ctx, err.extended_code);
|
||||||
|
if let Some(Ok(cstr)) = s.as_ref().map(|s| str_to_cstring(s)) {
|
||||||
|
ffi::sqlite3_result_error(ctx, cstr.as_ptr(), -1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_ => {
|
||||||
|
ffi::sqlite3_result_error_code(ctx, constraint_error_code());
|
||||||
|
if let Ok(cstr) = str_to_cstring(err.description()) {
|
||||||
|
ffi::sqlite3_result_error(ctx, cstr.as_ptr(), -1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
unsafe extern "C" fn free_boxed_value<T>(p: *mut c_void) {
|
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);
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,8 @@
|
|||||||
use std::{convert, fmt, mem, ptr, result, str};
|
use std::{convert, fmt, mem, ptr, result, str};
|
||||||
use std::ffi::CStr;
|
use std::ffi::CStr;
|
||||||
use std::os::raw::{c_char, c_int, c_void};
|
use std::os::raw::{c_char, c_int, c_void};
|
||||||
|
#[cfg(feature = "array")]
|
||||||
|
use std::rc::Rc;
|
||||||
use std::slice::from_raw_parts;
|
use std::slice::from_raw_parts;
|
||||||
|
|
||||||
use super::ffi;
|
use super::ffi;
|
||||||
@ -8,6 +10,8 @@ use super::{Connection, RawStatement, Result, Error, ValueRef, Row, Rows, AndThe
|
|||||||
use super::str_to_cstring;
|
use super::str_to_cstring;
|
||||||
use types::{ToSql, ToSqlOutput};
|
use types::{ToSql, ToSqlOutput};
|
||||||
use row::{RowsCrateImpl, MappedRowsCrateImpl, AndThenRowsCrateImpl};
|
use row::{RowsCrateImpl, MappedRowsCrateImpl, AndThenRowsCrateImpl};
|
||||||
|
#[cfg(feature = "array")]
|
||||||
|
use vtab::array::{ARRAY_TYPE, free_array};
|
||||||
|
|
||||||
/// A prepared statement.
|
/// A prepared statement.
|
||||||
pub struct Statement<'conn> {
|
pub struct Statement<'conn> {
|
||||||
@ -404,6 +408,11 @@ impl<'conn> Statement<'conn> {
|
|||||||
return self.conn
|
return self.conn
|
||||||
.decode_result(unsafe { ffi::sqlite3_bind_zeroblob(ptr, col, len) });
|
.decode_result(unsafe { ffi::sqlite3_bind_zeroblob(ptr, col, len) });
|
||||||
}
|
}
|
||||||
|
#[cfg(feature = "array")]
|
||||||
|
ToSqlOutput::Array(a) => {
|
||||||
|
return self.conn
|
||||||
|
.decode_result(unsafe { ffi::sqlite3_bind_pointer(ptr, col, Rc::into_raw(a) as *mut c_void, ARRAY_TYPE, Some(free_array)) });
|
||||||
|
}
|
||||||
};
|
};
|
||||||
self.conn
|
self.conn
|
||||||
.decode_result(match value {
|
.decode_result(match value {
|
||||||
@ -442,7 +451,7 @@ impl<'conn> Statement<'conn> {
|
|||||||
ffi::SQLITE_TRANSIENT())
|
ffi::SQLITE_TRANSIENT())
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
fn execute_with_bound_parameters(&mut self) -> Result<c_int> {
|
fn execute_with_bound_parameters(&mut self) -> Result<c_int> {
|
||||||
|
@ -1,4 +1,6 @@
|
|||||||
use super::{Null, Value, ValueRef};
|
use super::{Null, Value, ValueRef};
|
||||||
|
#[cfg(feature = "array")]
|
||||||
|
use vtab::array::Array;
|
||||||
use Result;
|
use Result;
|
||||||
|
|
||||||
/// `ToSqlOutput` represents the possible output types for implementors of the `ToSql` trait.
|
/// `ToSqlOutput` represents the possible output types for implementors of the `ToSql` trait.
|
||||||
@ -13,6 +15,9 @@ pub enum ToSqlOutput<'a> {
|
|||||||
/// A BLOB of the given length that is filled with zeroes.
|
/// A BLOB of the given length that is filled with zeroes.
|
||||||
#[cfg(feature = "blob")]
|
#[cfg(feature = "blob")]
|
||||||
ZeroBlob(i32),
|
ZeroBlob(i32),
|
||||||
|
|
||||||
|
#[cfg(feature = "array")]
|
||||||
|
Array(Array)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Generically allow any type that can be converted into a ValueRef
|
// Generically allow any type that can be converted into a ValueRef
|
||||||
@ -59,6 +64,8 @@ impl<'a> ToSql for ToSqlOutput<'a> {
|
|||||||
|
|
||||||
#[cfg(feature = "blob")]
|
#[cfg(feature = "blob")]
|
||||||
ToSqlOutput::ZeroBlob(i) => ToSqlOutput::ZeroBlob(i),
|
ToSqlOutput::ZeroBlob(i) => ToSqlOutput::ZeroBlob(i),
|
||||||
|
#[cfg(feature = "array")]
|
||||||
|
ToSqlOutput::Array(ref a) => ToSqlOutput::Array(a.clone()),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
179
src/vtab/array.rs
Normal file
179
src/vtab/array.rs
Normal file
@ -0,0 +1,179 @@
|
|||||||
|
//! Array Virtual Table
|
||||||
|
//! Port of [carray](http://www.sqlite.org/cgi/src/finfo?name=ext/misc/carray.c) C extension.
|
||||||
|
use std::default::Default;
|
||||||
|
use std::os::raw::{c_char, c_int, c_void};
|
||||||
|
use std::rc::Rc;
|
||||||
|
|
||||||
|
use ffi;
|
||||||
|
use types::{ToSql, ToSqlOutput, Value};
|
||||||
|
use vtab::{self, declare_vtab, Context, IndexInfo, VTab, VTabCursor, Values};
|
||||||
|
use {Connection, Error, Result};
|
||||||
|
|
||||||
|
// http://sqlite.org/bindptr.html
|
||||||
|
|
||||||
|
pub(crate) const ARRAY_TYPE: *const c_char = b"rarray\0" as *const u8 as *const c_char;
|
||||||
|
|
||||||
|
pub(crate) unsafe extern "C" fn free_array(p: *mut c_void) {
|
||||||
|
let _: Array = Rc::from_raw(p as *const Vec<Value>);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub type Array = Rc<Vec<Value>>;
|
||||||
|
|
||||||
|
impl ToSql for Array {
|
||||||
|
fn to_sql(&self) -> Result<ToSqlOutput> {
|
||||||
|
Ok(ToSqlOutput::Array(self.clone()))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Register the "rarray" module.
|
||||||
|
pub fn load_module(conn: &Connection) -> Result<()> {
|
||||||
|
let aux: Option<()> = None;
|
||||||
|
conn.create_module("rarray", &ARRAY_MODULE, aux)
|
||||||
|
}
|
||||||
|
|
||||||
|
eponymous_module!(
|
||||||
|
ARRAY_MODULE,
|
||||||
|
ArrayTab,
|
||||||
|
(),
|
||||||
|
ArrayTabCursor,
|
||||||
|
None,
|
||||||
|
array_connect,
|
||||||
|
array_best_index,
|
||||||
|
array_disconnect,
|
||||||
|
None,
|
||||||
|
array_open,
|
||||||
|
array_close,
|
||||||
|
array_filter,
|
||||||
|
array_next,
|
||||||
|
array_eof,
|
||||||
|
array_column,
|
||||||
|
array_rowid
|
||||||
|
);
|
||||||
|
|
||||||
|
// Column numbers
|
||||||
|
// const CARRAY_COLUMN_VALUE : c_int = 0;
|
||||||
|
const CARRAY_COLUMN_POINTER: c_int = 1;
|
||||||
|
|
||||||
|
/// An instance of the Array virtual table
|
||||||
|
#[repr(C)]
|
||||||
|
struct ArrayTab {
|
||||||
|
/// Base class. Must be first
|
||||||
|
base: ffi::sqlite3_vtab,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl VTab for ArrayTab {
|
||||||
|
type Aux = ();
|
||||||
|
type Cursor = ArrayTabCursor;
|
||||||
|
|
||||||
|
unsafe fn connect(db: *mut ffi::sqlite3, _aux: *mut (), _args: &[&[u8]]) -> Result<ArrayTab> {
|
||||||
|
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;
|
||||||
|
for (i, constraint) in info.constraints().enumerate() {
|
||||||
|
if !constraint.is_usable() {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if constraint.operator() != vtab::IndexConstraintOp::SQLITE_INDEX_CONSTRAINT_EQ {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
match constraint.column() {
|
||||||
|
CARRAY_COLUMN_POINTER => {
|
||||||
|
ptr_idx = Some(i);
|
||||||
|
}
|
||||||
|
_ => {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if let Some(ptr_idx) = ptr_idx {
|
||||||
|
{
|
||||||
|
let mut constraint_usage = info.constraint_usage(ptr_idx);
|
||||||
|
constraint_usage.set_argv_index(1);
|
||||||
|
constraint_usage.set_omit(true);
|
||||||
|
}
|
||||||
|
info.set_estimated_cost(1f64);
|
||||||
|
info.set_estimated_rows(100);
|
||||||
|
info.set_idx_num(1);
|
||||||
|
} else {
|
||||||
|
info.set_estimated_cost(2_147_483_647f64);
|
||||||
|
info.set_estimated_rows(2_147_483_647);
|
||||||
|
info.set_idx_num(0);
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn open(&self) -> Result<ArrayTabCursor> {
|
||||||
|
Ok(ArrayTabCursor::new())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// A cursor for the Array virtual table
|
||||||
|
#[repr(C)]
|
||||||
|
struct ArrayTabCursor {
|
||||||
|
/// Base class. Must be first
|
||||||
|
base: ffi::sqlite3_vtab_cursor,
|
||||||
|
/// The rowid
|
||||||
|
row_id: i64,
|
||||||
|
/// Pointer to the array of values ("pointer")
|
||||||
|
ptr: Option<Array>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ArrayTabCursor {
|
||||||
|
fn new() -> ArrayTabCursor {
|
||||||
|
ArrayTabCursor {
|
||||||
|
base: Default::default(),
|
||||||
|
row_id: 0,
|
||||||
|
ptr: None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
fn len(&self) -> i64 {
|
||||||
|
match self.ptr {
|
||||||
|
Some(ref a) => a.len() as i64,
|
||||||
|
_ => 0,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl VTabCursor for ArrayTabCursor {
|
||||||
|
type Table = ArrayTab;
|
||||||
|
|
||||||
|
fn vtab(&self) -> &ArrayTab {
|
||||||
|
unsafe { &*(self.base.pVtab as *const ArrayTab) }
|
||||||
|
}
|
||||||
|
fn filter(&mut self, idx_num: c_int, _idx_str: Option<&str>, args: &Values) -> Result<()> {
|
||||||
|
if idx_num > 0 {
|
||||||
|
self.ptr = try!(args.get_array(0));
|
||||||
|
} else {
|
||||||
|
self.ptr = None;
|
||||||
|
}
|
||||||
|
self.row_id = 1;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
fn next(&mut self) -> Result<()> {
|
||||||
|
self.row_id += 1;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
fn eof(&self) -> bool {
|
||||||
|
self.row_id > self.len()
|
||||||
|
}
|
||||||
|
fn column(&self, ctx: &mut Context, i: c_int) -> Result<()> {
|
||||||
|
match i {
|
||||||
|
CARRAY_COLUMN_POINTER => Ok(()),
|
||||||
|
_ => {
|
||||||
|
if let Some(ref array) = self.ptr {
|
||||||
|
let value = &array[i as usize];
|
||||||
|
ctx.set_result(&value)
|
||||||
|
} else {
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
fn rowid(&self) -> Result<i64> {
|
||||||
|
Ok(self.row_id)
|
||||||
|
}
|
||||||
|
}
|
@ -327,12 +327,10 @@ impl VTabCursor for CSVTabCursor {
|
|||||||
)));
|
)));
|
||||||
}
|
}
|
||||||
if self.cols.is_empty() {
|
if self.cols.is_empty() {
|
||||||
ctx.set_result(&Null);
|
return ctx.set_result(&Null);
|
||||||
return Ok(());
|
|
||||||
}
|
}
|
||||||
// TODO Affinity
|
// TODO Affinity
|
||||||
ctx.set_result(&self.cols[col as usize].to_owned());
|
ctx.set_result(&self.cols[col as usize].to_owned())
|
||||||
Ok(())
|
|
||||||
}
|
}
|
||||||
fn rowid(&self) -> Result<i64> {
|
fn rowid(&self) -> Result<i64> {
|
||||||
Ok(self.row_number as i64)
|
Ok(self.row_number as i64)
|
||||||
@ -365,7 +363,8 @@ mod test {
|
|||||||
assert_eq!(vec!["rowid", "colA", "colB", "colC"], headers);
|
assert_eq!(vec!["rowid", "colA", "colB", "colC"], headers);
|
||||||
}
|
}
|
||||||
|
|
||||||
let ids: Result<Vec<i32>> = s.query_map(&[], |row| row.get::<i32, i32>(0))
|
let ids: Result<Vec<i32>> = s
|
||||||
|
.query_map(&[], |row| row.get::<i32, i32>(0))
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.collect();
|
.collect();
|
||||||
let sum = ids.unwrap().iter().fold(0, |acc, &id| acc + id);
|
let sum = ids.unwrap().iter().fold(0, |acc, &id| acc + id);
|
||||||
@ -382,10 +381,11 @@ mod test {
|
|||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
{
|
{
|
||||||
let mut s = db.prepare(
|
let mut s =
|
||||||
"SELECT v1.rowid, v1.* FROM vtab v1 NATURAL JOIN vtab v2 WHERE \
|
db.prepare(
|
||||||
v1.rowid < v2.rowid",
|
"SELECT v1.rowid, v1.* FROM vtab v1 NATURAL JOIN vtab v2 WHERE \
|
||||||
).unwrap();
|
v1.rowid < v2.rowid",
|
||||||
|
).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();
|
||||||
|
@ -139,9 +139,8 @@ impl VTabCursor for IntArrayVTabCursor {
|
|||||||
let vtab = self.vtab();
|
let vtab = self.vtab();
|
||||||
unsafe {
|
unsafe {
|
||||||
let array = (*vtab.array).borrow();
|
let array = (*vtab.array).borrow();
|
||||||
ctx.set_result(&array[self.i]);
|
ctx.set_result(&array[self.i])
|
||||||
}
|
}
|
||||||
Ok(())
|
|
||||||
}
|
}
|
||||||
fn rowid(&self) -> Result<i64> {
|
fn rowid(&self) -> Result<i64> {
|
||||||
Ok(self.i as i64)
|
Ok(self.i as i64)
|
||||||
|
@ -6,7 +6,7 @@ use std::os::raw::{c_char, c_int, c_void};
|
|||||||
use std::ptr;
|
use std::ptr;
|
||||||
use std::slice;
|
use std::slice;
|
||||||
|
|
||||||
use context::{report_error, set_result};
|
use context::set_result;
|
||||||
use error::error_from_sqlite_code;
|
use error::error_from_sqlite_code;
|
||||||
use ffi;
|
use ffi;
|
||||||
use types::{FromSql, FromSqlError, ToSql, ValueRef};
|
use types::{FromSql, FromSqlError, ToSql, ValueRef};
|
||||||
@ -207,12 +207,10 @@ pub trait VTabCursor: Sized {
|
|||||||
pub struct Context(*mut ffi::sqlite3_context);
|
pub struct Context(*mut ffi::sqlite3_context);
|
||||||
|
|
||||||
impl Context {
|
impl Context {
|
||||||
pub fn set_result<T: ToSql>(&mut self, value: &T) {
|
pub fn set_result<T: ToSql>(&mut self, value: &T) -> Result<()> {
|
||||||
let t = value.to_sql();
|
let t = value.to_sql()?;
|
||||||
match t {
|
unsafe { set_result(self.0, &t) };
|
||||||
Ok(ref value) => unsafe { set_result(self.0, value) },
|
Ok(())
|
||||||
Err(err) => unsafe { report_error(self.0, &err) },
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -241,6 +239,22 @@ impl<'a> Values<'a> {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// `sqlite3_value_type` returns `SQLITE_NULL` for pointer.
|
||||||
|
// So it seems not possible to enhance `ValueRef::from_value`.
|
||||||
|
#[cfg(feature = "array")]
|
||||||
|
pub fn get_array(&self, idx: usize) -> Result<Option<array::Array>> {
|
||||||
|
use types::Value;
|
||||||
|
let arg = self.args[idx];
|
||||||
|
let ptr = unsafe { ffi::sqlite3_value_pointer(arg, array::ARRAY_TYPE) };
|
||||||
|
if ptr.is_null() {
|
||||||
|
Ok(None)
|
||||||
|
} else {
|
||||||
|
Ok(Some(unsafe {
|
||||||
|
array::Array::from_raw(ptr as *const Vec<Value>)
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub fn iter(&self) -> ValueIter {
|
pub fn iter(&self) -> ValueIter {
|
||||||
ValueIter {
|
ValueIter {
|
||||||
iter: self.args.iter(),
|
iter: self.args.iter(),
|
||||||
@ -361,12 +375,16 @@ 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")
|
if s.eq_ignore_ascii_case("yes")
|
||||||
|| s.eq_ignore_ascii_case("true") || s.eq("1")
|
|| 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")
|
} else if s.eq_ignore_ascii_case("no")
|
||||||
|| s.eq_ignore_ascii_case("false") || s.eq("0")
|
|| s.eq_ignore_ascii_case("off")
|
||||||
|
|| s.eq_ignore_ascii_case("false")
|
||||||
|
|| s.eq("0")
|
||||||
{
|
{
|
||||||
Some(false)
|
Some(false)
|
||||||
} else {
|
} else {
|
||||||
@ -530,7 +548,8 @@ macro_rules! create_or_connect {
|
|||||||
|
|
||||||
let aux = aux as *mut $aux;
|
let aux = aux as *mut $aux;
|
||||||
let args = slice::from_raw_parts(argv, argc as usize);
|
let args = slice::from_raw_parts(argv, argc as usize);
|
||||||
let vec = args.iter()
|
let vec = args
|
||||||
|
.iter()
|
||||||
.map(|&cs| 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[..]) {
|
||||||
@ -756,6 +775,8 @@ 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()) }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "array")]
|
||||||
|
pub mod array;
|
||||||
#[cfg(feature = "csvtab")]
|
#[cfg(feature = "csvtab")]
|
||||||
pub mod csvtab;
|
pub mod csvtab;
|
||||||
pub mod int_array;
|
pub mod int_array;
|
||||||
|
@ -255,8 +255,7 @@ impl VTabCursor for SeriesTabCursor {
|
|||||||
SERIES_COLUMN_STEP => self.step,
|
SERIES_COLUMN_STEP => self.step,
|
||||||
_ => self.value,
|
_ => self.value,
|
||||||
};
|
};
|
||||||
ctx.set_result(&x);
|
ctx.set_result(&x)
|
||||||
Ok(())
|
|
||||||
}
|
}
|
||||||
fn rowid(&self) -> Result<i64> {
|
fn rowid(&self) -> Result<i64> {
|
||||||
Ok(self.row_id)
|
Ok(self.row_id)
|
||||||
|
Loading…
Reference in New Issue
Block a user