mirror of
https://github.com/isar/rusqlite.git
synced 2024-11-23 09:09:19 +08:00
commit
5b9f915cef
@ -3,6 +3,8 @@
|
|||||||
* BREAKING CHANGE: The `FromSql` trait has been redesigned. It now requires a single, safe
|
* BREAKING CHANGE: The `FromSql` trait has been redesigned. It now requires a single, safe
|
||||||
method instead of the previous definition which required implementing one or two unsafe
|
method instead of the previous definition which required implementing one or two unsafe
|
||||||
methods.
|
methods.
|
||||||
|
* BREAKING CHANGE: The `ToSql` trait has been redesigned. It can now be implemented without
|
||||||
|
`unsafe`, and implementors can choose to return either borrowed or owned results.
|
||||||
* Added `#[deprecated(since = "...", note = "...")]` flags (new in Rust 1.9 for libraries) to
|
* Added `#[deprecated(since = "...", note = "...")]` flags (new in Rust 1.9 for libraries) to
|
||||||
all deprecated APIs.
|
all deprecated APIs.
|
||||||
|
|
||||||
|
@ -52,10 +52,9 @@ use std::io;
|
|||||||
use std::cmp::min;
|
use std::cmp::min;
|
||||||
use std::mem;
|
use std::mem;
|
||||||
use std::ptr;
|
use std::ptr;
|
||||||
use libc::c_int;
|
|
||||||
|
|
||||||
use super::ffi;
|
use super::ffi;
|
||||||
use super::types::ToSql;
|
use super::types::{ToSql, ToSqlOutput};
|
||||||
use {Result, Connection, DatabaseName};
|
use {Result, Connection, DatabaseName};
|
||||||
|
|
||||||
/// Handle to an open BLOB.
|
/// Handle to an open BLOB.
|
||||||
@ -244,9 +243,9 @@ impl<'conn> Drop for Blob<'conn> {
|
|||||||
pub struct ZeroBlob(pub i32);
|
pub struct ZeroBlob(pub i32);
|
||||||
|
|
||||||
impl ToSql for ZeroBlob {
|
impl ToSql for ZeroBlob {
|
||||||
unsafe fn bind_parameter(&self, stmt: *mut ffi::sqlite3_stmt, col: c_int) -> c_int {
|
fn to_sql(&self) -> Result<ToSqlOutput> {
|
||||||
let ZeroBlob(length) = *self;
|
let ZeroBlob(length) = *self;
|
||||||
ffi::sqlite3_bind_zeroblob(stmt, col, length)
|
Ok(ToSqlOutput::ZeroBlob(length))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
213
src/functions.rs
213
src/functions.rs
@ -54,105 +54,79 @@ use std::ffi::CStr;
|
|||||||
use std::mem;
|
use std::mem;
|
||||||
use std::ptr;
|
use std::ptr;
|
||||||
use std::slice;
|
use std::slice;
|
||||||
use libc::{c_int, c_double, c_char, c_void};
|
use libc::{c_int, c_char, c_void};
|
||||||
|
|
||||||
use ffi;
|
use ffi;
|
||||||
pub use ffi::sqlite3_context;
|
use ffi::sqlite3_context;
|
||||||
pub use ffi::sqlite3_value;
|
use ffi::sqlite3_value;
|
||||||
pub use ffi::sqlite3_value_type;
|
|
||||||
pub use ffi::sqlite3_value_numeric_type;
|
|
||||||
|
|
||||||
use types::{Null, FromSql, ValueRef};
|
use types::{ToSql, ToSqlOutput, FromSql, ValueRef};
|
||||||
|
|
||||||
use {Result, Error, Connection, str_to_cstring, InnerConnection};
|
use {Result, Error, Connection, str_to_cstring, InnerConnection};
|
||||||
|
|
||||||
/// A trait for types that can be converted into the result of an SQL function.
|
fn set_result<'a>(ctx: *mut sqlite3_context, result: &ToSqlOutput<'a>) {
|
||||||
pub trait ToResult {
|
let value = match *result {
|
||||||
unsafe fn set_result(&self, ctx: *mut sqlite3_context);
|
ToSqlOutput::Borrowed(v) => v,
|
||||||
}
|
ToSqlOutput::Owned(ref v) => ValueRef::from(v),
|
||||||
|
|
||||||
macro_rules! raw_to_impl(
|
#[cfg(feature = "blob")]
|
||||||
($t:ty, $f:ident) => (
|
ToSqlOutput::ZeroBlob(len) => {
|
||||||
impl ToResult for $t {
|
return unsafe { ffi::sqlite3_result_zeroblob(ctx, len) };
|
||||||
unsafe fn set_result(&self, ctx: *mut sqlite3_context) {
|
|
||||||
ffi::$f(ctx, *self)
|
|
||||||
}
|
}
|
||||||
}
|
};
|
||||||
)
|
|
||||||
);
|
|
||||||
|
|
||||||
raw_to_impl!(c_int, sqlite3_result_int);
|
match value {
|
||||||
raw_to_impl!(i64, sqlite3_result_int64);
|
ValueRef::Null => unsafe { ffi::sqlite3_result_null(ctx) },
|
||||||
raw_to_impl!(c_double, sqlite3_result_double);
|
ValueRef::Integer(i) => unsafe { ffi::sqlite3_result_int64(ctx, i) },
|
||||||
|
ValueRef::Real(r) => unsafe { ffi::sqlite3_result_double(ctx, r) },
|
||||||
impl<'a> ToResult for bool {
|
ValueRef::Text(ref s) => unsafe {
|
||||||
unsafe fn set_result(&self, ctx: *mut sqlite3_context) {
|
let length = s.len();
|
||||||
if *self {
|
|
||||||
ffi::sqlite3_result_int(ctx, 1)
|
|
||||||
} else {
|
|
||||||
ffi::sqlite3_result_int(ctx, 0)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
impl<'a> ToResult for &'a str {
|
|
||||||
unsafe fn set_result(&self, ctx: *mut sqlite3_context) {
|
|
||||||
let length = self.len();
|
|
||||||
if length > ::std::i32::MAX as usize {
|
if length > ::std::i32::MAX as usize {
|
||||||
ffi::sqlite3_result_error_toobig(ctx);
|
ffi::sqlite3_result_error_toobig(ctx);
|
||||||
return;
|
} else {
|
||||||
}
|
let c_str = match str_to_cstring(s) {
|
||||||
match str_to_cstring(self) {
|
Ok(c_str) => c_str,
|
||||||
Ok(c_str) => {
|
|
||||||
ffi::sqlite3_result_text(ctx,
|
|
||||||
c_str.as_ptr(),
|
|
||||||
length as c_int,
|
|
||||||
ffi::SQLITE_TRANSIENT())
|
|
||||||
}
|
|
||||||
// TODO sqlite3_result_error
|
// TODO sqlite3_result_error
|
||||||
Err(_) => ffi::sqlite3_result_error_code(ctx, ffi::SQLITE_MISUSE),
|
Err(_) => return ffi::sqlite3_result_error_code(ctx, ffi::SQLITE_MISUSE),
|
||||||
|
};
|
||||||
|
let destructor = if length > 0 {
|
||||||
|
ffi::SQLITE_TRANSIENT()
|
||||||
|
} else {
|
||||||
|
ffi::SQLITE_STATIC()
|
||||||
|
};
|
||||||
|
ffi::sqlite3_result_text(ctx, c_str.as_ptr(), length as c_int, destructor);
|
||||||
}
|
}
|
||||||
}
|
},
|
||||||
}
|
ValueRef::Blob(ref b) => unsafe {
|
||||||
|
let length = b.len();
|
||||||
impl ToResult for String {
|
if length > ::std::i32::MAX as usize {
|
||||||
unsafe fn set_result(&self, ctx: *mut sqlite3_context) {
|
|
||||||
(&self[..]).set_result(ctx)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a> ToResult for &'a [u8] {
|
|
||||||
unsafe fn set_result(&self, ctx: *mut sqlite3_context) {
|
|
||||||
if self.len() > ::std::i32::MAX as usize {
|
|
||||||
ffi::sqlite3_result_error_toobig(ctx);
|
ffi::sqlite3_result_error_toobig(ctx);
|
||||||
return;
|
} else if length == 0 {
|
||||||
}
|
ffi::sqlite3_result_zeroblob(ctx, 0)
|
||||||
|
} else {
|
||||||
ffi::sqlite3_result_blob(ctx,
|
ffi::sqlite3_result_blob(ctx,
|
||||||
mem::transmute(self.as_ptr()),
|
b.as_ptr() as *const c_void,
|
||||||
self.len() as c_int,
|
length as c_int,
|
||||||
ffi::SQLITE_TRANSIENT())
|
ffi::SQLITE_TRANSIENT());
|
||||||
|
}
|
||||||
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ToResult for Vec<u8> {
|
unsafe fn report_error(ctx: *mut sqlite3_context, err: &Error) {
|
||||||
unsafe fn set_result(&self, ctx: *mut sqlite3_context) {
|
match err {
|
||||||
(&self[..]).set_result(ctx)
|
&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);
|
||||||
impl<T: ToResult> ToResult for Option<T> {
|
}
|
||||||
unsafe fn set_result(&self, ctx: *mut sqlite3_context) {
|
}
|
||||||
match *self {
|
_ => {
|
||||||
None => ffi::sqlite3_result_null(ctx),
|
ffi::sqlite3_result_error_code(ctx, ffi::SQLITE_CONSTRAINT_FUNCTION);
|
||||||
Some(ref t) => t.set_result(ctx),
|
if let Ok(cstr) = str_to_cstring(err.description()) {
|
||||||
|
ffi::sqlite3_result_error(ctx, cstr.as_ptr(), -1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
impl ToResult for Null {
|
|
||||||
unsafe fn set_result(&self, ctx: *mut sqlite3_context) {
|
|
||||||
ffi::sqlite3_result_null(ctx)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -166,7 +140,8 @@ impl<'a> ValueRef<'a> {
|
|||||||
ffi::SQLITE_FLOAT => ValueRef::Real(ffi::sqlite3_value_double(value)),
|
ffi::SQLITE_FLOAT => ValueRef::Real(ffi::sqlite3_value_double(value)),
|
||||||
ffi::SQLITE_TEXT => {
|
ffi::SQLITE_TEXT => {
|
||||||
let text = ffi::sqlite3_value_text(value);
|
let text = ffi::sqlite3_value_text(value);
|
||||||
assert!(!text.is_null(), "unexpected SQLITE_TEXT value type with NULL data");
|
assert!(!text.is_null(),
|
||||||
|
"unexpected SQLITE_TEXT value type with NULL data");
|
||||||
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.
|
||||||
@ -175,14 +150,16 @@ impl<'a> ValueRef<'a> {
|
|||||||
}
|
}
|
||||||
ffi::SQLITE_BLOB => {
|
ffi::SQLITE_BLOB => {
|
||||||
let blob = ffi::sqlite3_value_blob(value);
|
let blob = ffi::sqlite3_value_blob(value);
|
||||||
assert!(!blob.is_null(), "unexpected SQLITE_BLOB value type with NULL data");
|
assert!(!blob.is_null(),
|
||||||
|
"unexpected SQLITE_BLOB value type with NULL data");
|
||||||
|
|
||||||
let len = ffi::sqlite3_value_bytes(value);
|
let len = ffi::sqlite3_value_bytes(value);
|
||||||
assert!(len >= 0, "unexpected negative return from sqlite3_value_bytes");
|
assert!(len >= 0,
|
||||||
|
"unexpected negative return from sqlite3_value_bytes");
|
||||||
|
|
||||||
ValueRef::Blob(from_raw_parts(blob as *const u8, len as usize))
|
ValueRef::Blob(from_raw_parts(blob as *const u8, len as usize))
|
||||||
}
|
}
|
||||||
_ => unreachable!("sqlite3_value_type returned invalid value")
|
_ => unreachable!("sqlite3_value_type returned invalid value"),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -219,7 +196,7 @@ impl<'a> Context<'a> {
|
|||||||
let value = unsafe { ValueRef::from_value(arg) };
|
let value = unsafe { ValueRef::from_value(arg) };
|
||||||
FromSql::column_result(value).map_err(|err| match err {
|
FromSql::column_result(value).map_err(|err| match err {
|
||||||
Error::InvalidColumnType => Error::InvalidFunctionParameterType,
|
Error::InvalidColumnType => Error::InvalidFunctionParameterType,
|
||||||
_ => err
|
_ => err,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -259,7 +236,7 @@ impl<'a> Context<'a> {
|
|||||||
/// `A` is the type of the aggregation context and `T` is the type of the final result.
|
/// `A` is the type of the aggregation context and `T` is the type of the final result.
|
||||||
/// Implementations should be stateless.
|
/// Implementations should be stateless.
|
||||||
pub trait Aggregate<A, T>
|
pub trait Aggregate<A, T>
|
||||||
where T: ToResult
|
where T: ToSql
|
||||||
{
|
{
|
||||||
/// Initializes the aggregation context. Will be called prior to the first call
|
/// Initializes the aggregation context. Will be called prior to the first call
|
||||||
/// to `step()` to set up the context for an invocation of the function. (Note:
|
/// to `step()` to set up the context for an invocation of the function. (Note:
|
||||||
@ -316,7 +293,7 @@ impl Connection {
|
|||||||
x_func: F)
|
x_func: F)
|
||||||
-> Result<()>
|
-> Result<()>
|
||||||
where F: FnMut(&Context) -> Result<T>,
|
where F: FnMut(&Context) -> Result<T>,
|
||||||
T: ToResult
|
T: ToSql
|
||||||
{
|
{
|
||||||
self.db.borrow_mut().create_scalar_function(fn_name, n_arg, deterministic, x_func)
|
self.db.borrow_mut().create_scalar_function(fn_name, n_arg, deterministic, x_func)
|
||||||
}
|
}
|
||||||
@ -333,7 +310,7 @@ impl Connection {
|
|||||||
aggr: D)
|
aggr: D)
|
||||||
-> Result<()>
|
-> Result<()>
|
||||||
where D: Aggregate<A, T>,
|
where D: Aggregate<A, T>,
|
||||||
T: ToResult
|
T: ToSql
|
||||||
{
|
{
|
||||||
self.db
|
self.db
|
||||||
.borrow_mut()
|
.borrow_mut()
|
||||||
@ -361,13 +338,13 @@ impl InnerConnection {
|
|||||||
x_func: F)
|
x_func: F)
|
||||||
-> Result<()>
|
-> Result<()>
|
||||||
where F: FnMut(&Context) -> Result<T>,
|
where F: FnMut(&Context) -> Result<T>,
|
||||||
T: ToResult
|
T: ToSql
|
||||||
{
|
{
|
||||||
unsafe extern "C" fn call_boxed_closure<F, T>(ctx: *mut sqlite3_context,
|
unsafe extern "C" fn call_boxed_closure<F, T>(ctx: *mut sqlite3_context,
|
||||||
argc: c_int,
|
argc: c_int,
|
||||||
argv: *mut *mut sqlite3_value)
|
argv: *mut *mut sqlite3_value)
|
||||||
where F: FnMut(&Context) -> Result<T>,
|
where F: FnMut(&Context) -> Result<T>,
|
||||||
T: ToResult
|
T: ToSql
|
||||||
{
|
{
|
||||||
let ctx = Context {
|
let ctx = Context {
|
||||||
ctx: ctx,
|
ctx: ctx,
|
||||||
@ -375,20 +352,14 @@ impl InnerConnection {
|
|||||||
};
|
};
|
||||||
let boxed_f: *mut F = mem::transmute(ffi::sqlite3_user_data(ctx.ctx));
|
let boxed_f: *mut F = mem::transmute(ffi::sqlite3_user_data(ctx.ctx));
|
||||||
assert!(!boxed_f.is_null(), "Internal error - null function pointer");
|
assert!(!boxed_f.is_null(), "Internal error - null function pointer");
|
||||||
match (*boxed_f)(&ctx) {
|
|
||||||
Ok(r) => r.set_result(ctx.ctx),
|
let t = (*boxed_f)(&ctx);
|
||||||
Err(Error::SqliteFailure(err, s)) => {
|
let t = t.as_ref().map(|t| ToSql::to_sql(t));
|
||||||
ffi::sqlite3_result_error_code(ctx.ctx, err.extended_code);
|
|
||||||
if let Some(Ok(cstr)) = s.map(|s| str_to_cstring(&s)) {
|
match t {
|
||||||
ffi::sqlite3_result_error(ctx.ctx, cstr.as_ptr(), -1);
|
Ok(Ok(ref value)) => set_result(ctx.ctx, value),
|
||||||
}
|
Ok(Err(err)) => report_error(ctx.ctx, &err),
|
||||||
}
|
Err(err) => report_error(ctx.ctx, err),
|
||||||
Err(err) => {
|
|
||||||
ffi::sqlite3_result_error_code(ctx.ctx, ffi::SQLITE_CONSTRAINT_FUNCTION);
|
|
||||||
if let Ok(cstr) = str_to_cstring(err.description()) {
|
|
||||||
ffi::sqlite3_result_error(ctx.ctx, cstr.as_ptr(), -1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -419,7 +390,7 @@ impl InnerConnection {
|
|||||||
aggr: D)
|
aggr: D)
|
||||||
-> Result<()>
|
-> Result<()>
|
||||||
where D: Aggregate<A, T>,
|
where D: Aggregate<A, T>,
|
||||||
T: ToResult
|
T: ToSql
|
||||||
{
|
{
|
||||||
unsafe fn aggregate_context<A>(ctx: *mut sqlite3_context,
|
unsafe fn aggregate_context<A>(ctx: *mut sqlite3_context,
|
||||||
bytes: usize)
|
bytes: usize)
|
||||||
@ -431,28 +402,11 @@ impl InnerConnection {
|
|||||||
Some(pac)
|
Some(pac)
|
||||||
}
|
}
|
||||||
|
|
||||||
unsafe fn report_aggregate_error(ctx: *mut sqlite3_context, err: Error) {
|
|
||||||
match err {
|
|
||||||
Error::SqliteFailure(err, s) => {
|
|
||||||
ffi::sqlite3_result_error_code(ctx, err.extended_code);
|
|
||||||
if let Some(Ok(cstr)) = s.map(|s| str_to_cstring(&s)) {
|
|
||||||
ffi::sqlite3_result_error(ctx, cstr.as_ptr(), -1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
_ => {
|
|
||||||
ffi::sqlite3_result_error_code(ctx, ffi::SQLITE_CONSTRAINT_FUNCTION);
|
|
||||||
if let Ok(cstr) = str_to_cstring(err.description()) {
|
|
||||||
ffi::sqlite3_result_error(ctx, cstr.as_ptr(), -1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
unsafe extern "C" fn call_boxed_step<A, D, T>(ctx: *mut sqlite3_context,
|
unsafe extern "C" fn call_boxed_step<A, D, T>(ctx: *mut sqlite3_context,
|
||||||
argc: c_int,
|
argc: c_int,
|
||||||
argv: *mut *mut sqlite3_value)
|
argv: *mut *mut sqlite3_value)
|
||||||
where D: Aggregate<A, T>,
|
where D: Aggregate<A, T>,
|
||||||
T: ToResult
|
T: ToSql
|
||||||
{
|
{
|
||||||
let boxed_aggr: *mut D = mem::transmute(ffi::sqlite3_user_data(ctx));
|
let boxed_aggr: *mut D = mem::transmute(ffi::sqlite3_user_data(ctx));
|
||||||
assert!(!boxed_aggr.is_null(),
|
assert!(!boxed_aggr.is_null(),
|
||||||
@ -477,13 +431,13 @@ impl InnerConnection {
|
|||||||
|
|
||||||
match (*boxed_aggr).step(&mut ctx, &mut **pac) {
|
match (*boxed_aggr).step(&mut ctx, &mut **pac) {
|
||||||
Ok(_) => {}
|
Ok(_) => {}
|
||||||
Err(err) => report_aggregate_error(ctx.ctx, err),
|
Err(err) => report_error(ctx.ctx, &err),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
unsafe extern "C" fn call_boxed_final<A, D, T>(ctx: *mut sqlite3_context)
|
unsafe extern "C" fn call_boxed_final<A, D, T>(ctx: *mut sqlite3_context)
|
||||||
where D: Aggregate<A, T>,
|
where D: Aggregate<A, T>,
|
||||||
T: ToResult
|
T: ToSql
|
||||||
{
|
{
|
||||||
let boxed_aggr: *mut D = mem::transmute(ffi::sqlite3_user_data(ctx));
|
let boxed_aggr: *mut D = mem::transmute(ffi::sqlite3_user_data(ctx));
|
||||||
assert!(!boxed_aggr.is_null(),
|
assert!(!boxed_aggr.is_null(),
|
||||||
@ -503,10 +457,13 @@ impl InnerConnection {
|
|||||||
None => None,
|
None => None,
|
||||||
};
|
};
|
||||||
|
|
||||||
match (*boxed_aggr).finalize(a) {
|
let t = (*boxed_aggr).finalize(a);
|
||||||
Ok(r) => r.set_result(ctx),
|
let t = t.as_ref().map(|t| ToSql::to_sql(t));
|
||||||
Err(err) => report_aggregate_error(ctx, err),
|
match t {
|
||||||
};
|
Ok(Ok(ref value)) => set_result(ctx, value),
|
||||||
|
Ok(Err(err)) => report_error(ctx, &err),
|
||||||
|
Err(err) => report_error(ctx, err),
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let boxed_aggr: *mut D = Box::into_raw(Box::new(aggr));
|
let boxed_aggr: *mut D = Box::into_raw(Box::new(aggr));
|
||||||
|
64
src/lib.rs
64
src/lib.rs
@ -71,9 +71,9 @@ use std::cell::RefCell;
|
|||||||
use std::ffi::{CStr, CString};
|
use std::ffi::{CStr, CString};
|
||||||
use std::result;
|
use std::result;
|
||||||
use std::str;
|
use std::str;
|
||||||
use libc::{c_int, c_char};
|
use libc::{c_int, c_char, c_void};
|
||||||
|
|
||||||
use types::{ToSql, FromSql, ValueRef};
|
use types::{ToSql, ToSqlOutput, FromSql, ValueRef};
|
||||||
use error::{error_from_sqlite_code, error_from_handle};
|
use error::{error_from_sqlite_code, error_from_handle};
|
||||||
use raw_statement::RawStatement;
|
use raw_statement::RawStatement;
|
||||||
use cache::StatementCache;
|
use cache::StatementCache;
|
||||||
@ -877,6 +877,55 @@ impl<'conn> Statement<'conn> {
|
|||||||
self.finalize_()
|
self.finalize_()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn bind_parameter(&self, param: &ToSql, col: c_int) -> Result<()> {
|
||||||
|
let value = try!(param.to_sql());
|
||||||
|
|
||||||
|
let ptr = unsafe { self.stmt.ptr() };
|
||||||
|
let value = match value {
|
||||||
|
ToSqlOutput::Borrowed(v) => v,
|
||||||
|
ToSqlOutput::Owned(ref v) => ValueRef::from(v),
|
||||||
|
|
||||||
|
#[cfg(feature = "blob")]
|
||||||
|
ToSqlOutput::ZeroBlob(len) => {
|
||||||
|
return self.conn
|
||||||
|
.decode_result(unsafe { ffi::sqlite3_bind_zeroblob(ptr, col, len) });
|
||||||
|
}
|
||||||
|
};
|
||||||
|
self.conn.decode_result(match value {
|
||||||
|
ValueRef::Null => unsafe { ffi::sqlite3_bind_null(ptr, col) },
|
||||||
|
ValueRef::Integer(i) => unsafe { ffi::sqlite3_bind_int64(ptr, col, i) },
|
||||||
|
ValueRef::Real(r) => unsafe { ffi::sqlite3_bind_double(ptr, col, r) },
|
||||||
|
ValueRef::Text(ref s) => unsafe {
|
||||||
|
let length = s.len();
|
||||||
|
if length > ::std::i32::MAX as usize {
|
||||||
|
ffi::SQLITE_TOOBIG
|
||||||
|
} else {
|
||||||
|
let c_str = try!(str_to_cstring(s));
|
||||||
|
let destructor = if length > 0 {
|
||||||
|
ffi::SQLITE_TRANSIENT()
|
||||||
|
} else {
|
||||||
|
ffi::SQLITE_STATIC()
|
||||||
|
};
|
||||||
|
ffi::sqlite3_bind_text(ptr, col, c_str.as_ptr(), length as c_int, destructor)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
ValueRef::Blob(ref b) => unsafe {
|
||||||
|
let length = b.len();
|
||||||
|
if length > ::std::i32::MAX as usize {
|
||||||
|
ffi::SQLITE_TOOBIG
|
||||||
|
} else if length == 0 {
|
||||||
|
ffi::sqlite3_bind_zeroblob(ptr, col, 0)
|
||||||
|
} else {
|
||||||
|
ffi::sqlite3_bind_blob(ptr,
|
||||||
|
col,
|
||||||
|
b.as_ptr() as *const c_void,
|
||||||
|
length as c_int,
|
||||||
|
ffi::SQLITE_TRANSIENT())
|
||||||
|
}
|
||||||
|
},
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
fn bind_parameters(&mut self, params: &[&ToSql]) -> Result<()> {
|
fn bind_parameters(&mut self, params: &[&ToSql]) -> Result<()> {
|
||||||
assert!(params.len() as c_int == self.stmt.bind_parameter_count(),
|
assert!(params.len() as c_int == self.stmt.bind_parameter_count(),
|
||||||
"incorrect number of parameters to query(): expected {}, got {}",
|
"incorrect number of parameters to query(): expected {}, got {}",
|
||||||
@ -884,11 +933,7 @@ impl<'conn> Statement<'conn> {
|
|||||||
params.len());
|
params.len());
|
||||||
|
|
||||||
for (i, p) in params.iter().enumerate() {
|
for (i, p) in params.iter().enumerate() {
|
||||||
try!(unsafe {
|
try!(self.bind_parameter(*p, (i + 1) as c_int));
|
||||||
self.conn.decode_result(
|
|
||||||
p.bind_parameter(self.stmt.ptr(), (i + 1) as c_int)
|
|
||||||
)
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
@ -1114,7 +1159,8 @@ impl<'a> ValueRef<'a> {
|
|||||||
ffi::SQLITE_FLOAT => ValueRef::Real(ffi::sqlite3_column_double(raw, col)),
|
ffi::SQLITE_FLOAT => ValueRef::Real(ffi::sqlite3_column_double(raw, col)),
|
||||||
ffi::SQLITE_TEXT => {
|
ffi::SQLITE_TEXT => {
|
||||||
let text = ffi::sqlite3_column_text(raw, col);
|
let text = ffi::sqlite3_column_text(raw, col);
|
||||||
assert!(!text.is_null(), "unexpected SQLITE_TEXT column type with NULL data");
|
assert!(!text.is_null(),
|
||||||
|
"unexpected SQLITE_TEXT column type with NULL data");
|
||||||
let s = CStr::from_ptr(text as *const c_char);
|
let s = CStr::from_ptr(text as *const c_char);
|
||||||
|
|
||||||
// sqlite3_column_text returns UTF8 data, so our unwrap here should be fine.
|
// sqlite3_column_text returns UTF8 data, so our unwrap here should be fine.
|
||||||
@ -1135,7 +1181,7 @@ impl<'a> ValueRef<'a> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
_ => unreachable!("sqlite3_column_type returned invalid value")
|
_ => unreachable!("sqlite3_column_type returned invalid value"),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -204,9 +204,7 @@ impl<'conn> Statement<'conn> {
|
|||||||
fn bind_parameters_named(&mut self, params: &[(&str, &ToSql)]) -> Result<()> {
|
fn bind_parameters_named(&mut self, params: &[(&str, &ToSql)]) -> Result<()> {
|
||||||
for &(name, value) in params {
|
for &(name, value) in params {
|
||||||
if let Some(i) = try!(self.parameter_index(name)) {
|
if let Some(i) = try!(self.parameter_index(name)) {
|
||||||
try!(self.conn.decode_result(unsafe {
|
try!(self.bind_parameter(value, i));
|
||||||
value.bind_parameter(self.stmt.ptr(), i)
|
|
||||||
}));
|
|
||||||
} else {
|
} else {
|
||||||
return Err(Error::InvalidParameterName(name.into()));
|
return Err(Error::InvalidParameterName(name.into()));
|
||||||
}
|
}
|
||||||
|
@ -4,18 +4,15 @@ extern crate chrono;
|
|||||||
use std::borrow::Cow;
|
use std::borrow::Cow;
|
||||||
|
|
||||||
use self::chrono::{NaiveDate, NaiveTime, NaiveDateTime, DateTime, TimeZone, UTC, Local};
|
use self::chrono::{NaiveDate, NaiveTime, NaiveDateTime, DateTime, TimeZone, UTC, Local};
|
||||||
use libc::c_int;
|
|
||||||
|
|
||||||
use {Error, Result};
|
use {Error, Result};
|
||||||
use types::{FromSql, ToSql, ValueRef};
|
use types::{FromSql, ToSql, ToSqlOutput, ValueRef};
|
||||||
|
|
||||||
use ffi::sqlite3_stmt;
|
|
||||||
|
|
||||||
/// ISO 8601 calendar date without timezone => "YYYY-MM-DD"
|
/// ISO 8601 calendar date without timezone => "YYYY-MM-DD"
|
||||||
impl ToSql for NaiveDate {
|
impl ToSql for NaiveDate {
|
||||||
unsafe fn bind_parameter(&self, stmt: *mut sqlite3_stmt, col: c_int) -> c_int {
|
fn to_sql(&self) -> Result<ToSqlOutput> {
|
||||||
let date_str = self.format("%Y-%m-%d").to_string();
|
let date_str = self.format("%Y-%m-%d").to_string();
|
||||||
date_str.bind_parameter(stmt, col)
|
Ok(ToSqlOutput::from(date_str))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -31,9 +28,9 @@ impl FromSql for NaiveDate {
|
|||||||
|
|
||||||
/// ISO 8601 time without timezone => "HH:MM:SS.SSS"
|
/// ISO 8601 time without timezone => "HH:MM:SS.SSS"
|
||||||
impl ToSql for NaiveTime {
|
impl ToSql for NaiveTime {
|
||||||
unsafe fn bind_parameter(&self, stmt: *mut sqlite3_stmt, col: c_int) -> c_int {
|
fn to_sql(&self) -> Result<ToSqlOutput> {
|
||||||
let date_str = self.format("%H:%M:%S%.f").to_string();
|
let date_str = self.format("%H:%M:%S%.f").to_string();
|
||||||
date_str.bind_parameter(stmt, col)
|
Ok(ToSqlOutput::from(date_str))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -56,9 +53,9 @@ impl FromSql for NaiveTime {
|
|||||||
|
|
||||||
/// ISO 8601 combined date and time without timezone => "YYYY-MM-DD HH:MM:SS.SSS"
|
/// ISO 8601 combined date and time without timezone => "YYYY-MM-DD HH:MM:SS.SSS"
|
||||||
impl ToSql for NaiveDateTime {
|
impl ToSql for NaiveDateTime {
|
||||||
unsafe fn bind_parameter(&self, stmt: *mut sqlite3_stmt, col: c_int) -> c_int {
|
fn to_sql(&self) -> Result<ToSqlOutput> {
|
||||||
let date_str = self.format("%Y-%m-%dT%H:%M:%S%.f").to_string();
|
let date_str = self.format("%Y-%m-%dT%H:%M:%S%.f").to_string();
|
||||||
date_str.bind_parameter(stmt, col)
|
Ok(ToSqlOutput::from(date_str))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -83,9 +80,8 @@ impl FromSql for NaiveDateTime {
|
|||||||
|
|
||||||
/// Date and time with time zone => UTC RFC3339 timestamp ("YYYY-MM-DDTHH:MM:SS.SSS+00:00").
|
/// Date and time with time zone => UTC RFC3339 timestamp ("YYYY-MM-DDTHH:MM:SS.SSS+00:00").
|
||||||
impl<Tz: TimeZone> ToSql for DateTime<Tz> {
|
impl<Tz: TimeZone> ToSql for DateTime<Tz> {
|
||||||
unsafe fn bind_parameter(&self, stmt: *mut sqlite3_stmt, col: c_int) -> c_int {
|
fn to_sql(&self) -> Result<ToSqlOutput> {
|
||||||
let utc_dt = self.with_timezone(&UTC);
|
Ok(ToSqlOutput::from(self.with_timezone(&UTC).to_rfc3339()))
|
||||||
utc_dt.to_rfc3339().bind_parameter(stmt, col)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -50,12 +50,12 @@
|
|||||||
//! `FromSql` for the cases where you want to know if a value was NULL (which gets translated to
|
//! `FromSql` for the cases where you want to know if a value was NULL (which gets translated to
|
||||||
//! `None`).
|
//! `None`).
|
||||||
|
|
||||||
pub use ffi::sqlite3_stmt;
|
|
||||||
|
|
||||||
pub use self::from_sql::FromSql;
|
pub use self::from_sql::FromSql;
|
||||||
pub use self::to_sql::ToSql;
|
pub use self::to_sql::{ToSql, ToSqlOutput};
|
||||||
|
pub use self::value::Value;
|
||||||
pub use self::value_ref::ValueRef;
|
pub use self::value_ref::ValueRef;
|
||||||
|
|
||||||
|
mod value;
|
||||||
mod value_ref;
|
mod value_ref;
|
||||||
mod from_sql;
|
mod from_sql;
|
||||||
mod to_sql;
|
mod to_sql;
|
||||||
@ -84,24 +84,6 @@ mod serde_json;
|
|||||||
#[derive(Copy,Clone)]
|
#[derive(Copy,Clone)]
|
||||||
pub struct Null;
|
pub struct Null;
|
||||||
|
|
||||||
/// Owning [dynamic type value](http://sqlite.org/datatype3.html). Value's type is typically
|
|
||||||
/// dictated by SQLite (not by the caller).
|
|
||||||
///
|
|
||||||
/// See [`ValueRef`](enum.ValueRef.html) for a non-owning dynamic type value.
|
|
||||||
#[derive(Clone,Debug,PartialEq)]
|
|
||||||
pub enum Value {
|
|
||||||
/// The value is a `NULL` value.
|
|
||||||
Null,
|
|
||||||
/// The value is a signed integer.
|
|
||||||
Integer(i64),
|
|
||||||
/// The value is a floating point number.
|
|
||||||
Real(f64),
|
|
||||||
/// The value is a text string.
|
|
||||||
Text(String),
|
|
||||||
/// The value is a blob of data
|
|
||||||
Blob(Vec<u8>),
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
#[cfg_attr(feature="clippy", allow(similar_names))]
|
#[cfg_attr(feature="clippy", allow(similar_names))]
|
||||||
mod test {
|
mod test {
|
||||||
@ -111,6 +93,7 @@ mod test {
|
|||||||
use Error;
|
use Error;
|
||||||
use libc::{c_int, c_double};
|
use libc::{c_int, c_double};
|
||||||
use std::f64::EPSILON;
|
use std::f64::EPSILON;
|
||||||
|
use super::Value;
|
||||||
|
|
||||||
fn checked_memory_handle() -> Connection {
|
fn checked_memory_handle() -> Connection {
|
||||||
let db = Connection::open_in_memory().unwrap();
|
let db = Connection::open_in_memory().unwrap();
|
||||||
@ -144,6 +127,17 @@ mod test {
|
|||||||
fn test_str() {
|
fn test_str() {
|
||||||
let db = checked_memory_handle();
|
let db = checked_memory_handle();
|
||||||
|
|
||||||
|
let s = "hello, world!";
|
||||||
|
db.execute("INSERT INTO foo(t) VALUES (?)", &[&s]).unwrap();
|
||||||
|
|
||||||
|
let from: String = db.query_row("SELECT t FROM foo", &[], |r| r.get(0)).unwrap();
|
||||||
|
assert_eq!(from, s);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_string() {
|
||||||
|
let db = checked_memory_handle();
|
||||||
|
|
||||||
let s = "hello, world!";
|
let s = "hello, world!";
|
||||||
db.execute("INSERT INTO foo(t) VALUES (?)", &[&s.to_owned()]).unwrap();
|
db.execute("INSERT INTO foo(t) VALUES (?)", &[&s.to_owned()]).unwrap();
|
||||||
|
|
||||||
@ -151,6 +145,16 @@ mod test {
|
|||||||
assert_eq!(from, s);
|
assert_eq!(from, s);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_value() {
|
||||||
|
let db = checked_memory_handle();
|
||||||
|
|
||||||
|
db.execute("INSERT INTO foo(i) VALUES (?)", &[&Value::Integer(10)]).unwrap();
|
||||||
|
|
||||||
|
assert_eq!(10i64,
|
||||||
|
db.query_row("SELECT i FROM foo", &[], |r| r.get(0)).unwrap());
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_option() {
|
fn test_option() {
|
||||||
let db = checked_memory_handle();
|
let db = checked_memory_handle();
|
||||||
|
@ -1,19 +1,15 @@
|
|||||||
//! `ToSql` and `FromSql` implementation for JSON `Value`.
|
//! `ToSql` and `FromSql` implementation for JSON `Value`.
|
||||||
extern crate serde_json;
|
extern crate serde_json;
|
||||||
|
|
||||||
use libc::c_int;
|
|
||||||
use self::serde_json::Value;
|
use self::serde_json::Value;
|
||||||
|
|
||||||
use {Error, Result};
|
use {Error, Result};
|
||||||
use types::{FromSql, ToSql, ValueRef};
|
use types::{FromSql, ToSql, ToSqlOutput, ValueRef};
|
||||||
|
|
||||||
use ffi::sqlite3_stmt;
|
|
||||||
|
|
||||||
/// Serialize JSON `Value` to text.
|
/// Serialize JSON `Value` to text.
|
||||||
impl ToSql for Value {
|
impl ToSql for Value {
|
||||||
unsafe fn bind_parameter(&self, stmt: *mut sqlite3_stmt, col: c_int) -> c_int {
|
fn to_sql(&self) -> Result<ToSqlOutput> {
|
||||||
let s = serde_json::to_string(self).unwrap();
|
Ok(ToSqlOutput::from(serde_json::to_string(self).unwrap()))
|
||||||
s.bind_parameter(stmt, col)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -24,7 +20,8 @@ impl FromSql for Value {
|
|||||||
ValueRef::Text(ref s) => serde_json::from_str(s),
|
ValueRef::Text(ref s) => serde_json::from_str(s),
|
||||||
ValueRef::Blob(ref b) => serde_json::from_slice(b),
|
ValueRef::Blob(ref b) => serde_json::from_slice(b),
|
||||||
_ => return Err(Error::InvalidColumnType),
|
_ => return Err(Error::InvalidColumnType),
|
||||||
}.map_err(|err| Error::FromSqlConversionFailure(Box::new(err)))
|
}
|
||||||
|
.map_err(|err| Error::FromSqlConversionFailure(Box::new(err)))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,17 +1,14 @@
|
|||||||
extern crate time;
|
extern crate time;
|
||||||
|
|
||||||
use libc::c_int;
|
|
||||||
use {Error, Result};
|
use {Error, Result};
|
||||||
use types::{FromSql, ToSql, ValueRef};
|
use types::{FromSql, ToSql, ToSqlOutput, ValueRef};
|
||||||
|
|
||||||
use ffi::sqlite3_stmt;
|
|
||||||
|
|
||||||
const SQLITE_DATETIME_FMT: &'static str = "%Y-%m-%d %H:%M:%S";
|
const SQLITE_DATETIME_FMT: &'static str = "%Y-%m-%d %H:%M:%S";
|
||||||
|
|
||||||
impl ToSql for time::Timespec {
|
impl ToSql for time::Timespec {
|
||||||
unsafe fn bind_parameter(&self, stmt: *mut sqlite3_stmt, col: c_int) -> c_int {
|
fn to_sql(&self) -> Result<ToSqlOutput> {
|
||||||
let time_str = time::at_utc(*self).strftime(SQLITE_DATETIME_FMT).unwrap().to_string();
|
let time_string = time::at_utc(*self).strftime(SQLITE_DATETIME_FMT).unwrap().to_string();
|
||||||
time_str.bind_parameter(stmt, col)
|
Ok(ToSqlOutput::from(time_string))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,95 +1,97 @@
|
|||||||
use std::mem;
|
use super::{Null, Value, ValueRef};
|
||||||
|
use ::Result;
|
||||||
|
|
||||||
use libc::{c_double, c_int};
|
/// `ToSqlOutput` represents the possible output types for implementors of the `ToSql` trait.
|
||||||
|
pub enum ToSqlOutput<'a> {
|
||||||
|
/// A borrowed SQLite-representable value.
|
||||||
|
Borrowed(ValueRef<'a>),
|
||||||
|
|
||||||
use super::Null;
|
/// An owned SQLite-representable value.
|
||||||
use ::{ffi, str_to_cstring};
|
Owned(Value),
|
||||||
use ffi::sqlite3_stmt;
|
|
||||||
|
/// A BLOB of the given length that is filled with zeroes.
|
||||||
|
#[cfg(feature = "blob")]
|
||||||
|
ZeroBlob(i32),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a, T: ?Sized> From<&'a T> for ToSqlOutput<'a>
|
||||||
|
where &'a T: Into<ValueRef<'a>>
|
||||||
|
{
|
||||||
|
fn from(t: &'a T) -> Self {
|
||||||
|
ToSqlOutput::Borrowed(t.into())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a, T: Into<Value>> From<T> for ToSqlOutput<'a> {
|
||||||
|
fn from(t: T) -> Self {
|
||||||
|
ToSqlOutput::Owned(t.into())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// A trait for types that can be converted into SQLite values.
|
/// A trait for types that can be converted into SQLite values.
|
||||||
pub trait ToSql {
|
pub trait ToSql {
|
||||||
unsafe fn bind_parameter(&self, stmt: *mut sqlite3_stmt, col: c_int) -> c_int;
|
fn to_sql(&self) -> Result<ToSqlOutput>;
|
||||||
}
|
}
|
||||||
|
|
||||||
macro_rules! raw_to_impl(
|
// We should be able to use a generic impl like this:
|
||||||
($t:ty, $f:ident) => (
|
//
|
||||||
|
// impl<T: Copy> ToSql for T where T: Into<Value> {
|
||||||
|
// fn to_sql(&self) -> Result<ToSqlOutput> {
|
||||||
|
// Ok(ToSqlOutput::from((*self).into()))
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// instead of the following macro, but this runs afoul of
|
||||||
|
// https://github.com/rust-lang/rust/issues/30191 and reports conflicting
|
||||||
|
// implementations even when there aren't any.
|
||||||
|
|
||||||
|
macro_rules! to_sql_self(
|
||||||
|
($t:ty) => (
|
||||||
impl ToSql for $t {
|
impl ToSql for $t {
|
||||||
unsafe fn bind_parameter(&self, stmt: *mut sqlite3_stmt, col: c_int) -> c_int {
|
fn to_sql(&self) -> Result<ToSqlOutput> {
|
||||||
ffi::$f(stmt, col, *self)
|
Ok(ToSqlOutput::from(*self))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
|
|
||||||
raw_to_impl!(c_int, sqlite3_bind_int); // i32
|
to_sql_self!(Null);
|
||||||
raw_to_impl!(i64, sqlite3_bind_int64);
|
to_sql_self!(bool);
|
||||||
raw_to_impl!(c_double, sqlite3_bind_double);
|
to_sql_self!(i32);
|
||||||
|
to_sql_self!(i64);
|
||||||
|
to_sql_self!(f64);
|
||||||
|
|
||||||
impl ToSql for bool {
|
impl<'a, T: ?Sized> ToSql for &'a T
|
||||||
unsafe fn bind_parameter(&self, stmt: *mut sqlite3_stmt, col: c_int) -> c_int {
|
where &'a T: Into<ToSqlOutput<'a>>
|
||||||
if *self {
|
{
|
||||||
ffi::sqlite3_bind_int(stmt, col, 1)
|
fn to_sql(&self) -> Result<ToSqlOutput> {
|
||||||
} else {
|
Ok(ToSqlOutput::from((*self).into()))
|
||||||
ffi::sqlite3_bind_int(stmt, col, 0)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a> ToSql for &'a str {
|
|
||||||
unsafe fn bind_parameter(&self, stmt: *mut sqlite3_stmt, col: c_int) -> c_int {
|
|
||||||
let length = self.len();
|
|
||||||
if length > ::std::i32::MAX as usize {
|
|
||||||
return ffi::SQLITE_TOOBIG;
|
|
||||||
}
|
|
||||||
match str_to_cstring(self) {
|
|
||||||
Ok(c_str) => {
|
|
||||||
ffi::sqlite3_bind_text(stmt,
|
|
||||||
col,
|
|
||||||
c_str.as_ptr(),
|
|
||||||
length as c_int,
|
|
||||||
ffi::SQLITE_TRANSIENT())
|
|
||||||
}
|
|
||||||
Err(_) => ffi::SQLITE_MISUSE,
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ToSql for String {
|
impl ToSql for String {
|
||||||
unsafe fn bind_parameter(&self, stmt: *mut sqlite3_stmt, col: c_int) -> c_int {
|
fn to_sql(&self) -> Result<ToSqlOutput> {
|
||||||
(&self[..]).bind_parameter(stmt, col)
|
Ok(ToSqlOutput::from(self.as_str()))
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a> ToSql for &'a [u8] {
|
|
||||||
unsafe fn bind_parameter(&self, stmt: *mut sqlite3_stmt, col: c_int) -> c_int {
|
|
||||||
if self.len() > ::std::i32::MAX as usize {
|
|
||||||
return ffi::SQLITE_TOOBIG;
|
|
||||||
}
|
|
||||||
ffi::sqlite3_bind_blob(stmt,
|
|
||||||
col,
|
|
||||||
mem::transmute(self.as_ptr()),
|
|
||||||
self.len() as c_int,
|
|
||||||
ffi::SQLITE_TRANSIENT())
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ToSql for Vec<u8> {
|
impl ToSql for Vec<u8> {
|
||||||
unsafe fn bind_parameter(&self, stmt: *mut sqlite3_stmt, col: c_int) -> c_int {
|
fn to_sql(&self) -> Result<ToSqlOutput> {
|
||||||
(&self[..]).bind_parameter(stmt, col)
|
Ok(ToSqlOutput::from(self.as_slice()))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ToSql for Value {
|
||||||
|
fn to_sql(&self) -> Result<ToSqlOutput> {
|
||||||
|
Ok(ToSqlOutput::from(self))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T: ToSql> ToSql for Option<T> {
|
impl<T: ToSql> ToSql for Option<T> {
|
||||||
unsafe fn bind_parameter(&self, stmt: *mut sqlite3_stmt, col: c_int) -> c_int {
|
fn to_sql(&self) -> Result<ToSqlOutput> {
|
||||||
match *self {
|
match *self {
|
||||||
None => ffi::sqlite3_bind_null(stmt, col),
|
None => Ok(ToSqlOutput::from(Null)),
|
||||||
Some(ref t) => t.bind_parameter(stmt, col),
|
Some(ref t) => t.to_sql(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ToSql for Null {
|
|
||||||
unsafe fn bind_parameter(&self, stmt: *mut sqlite3_stmt, col: c_int) -> c_int {
|
|
||||||
ffi::sqlite3_bind_null(stmt, col)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
61
src/types/value.rs
Normal file
61
src/types/value.rs
Normal file
@ -0,0 +1,61 @@
|
|||||||
|
use super::Null;
|
||||||
|
|
||||||
|
/// Owning [dynamic type value](http://sqlite.org/datatype3.html). Value's type is typically
|
||||||
|
/// dictated by SQLite (not by the caller).
|
||||||
|
///
|
||||||
|
/// See [`ValueRef`](enum.ValueRef.html) for a non-owning dynamic type value.
|
||||||
|
#[derive(Clone,Debug,PartialEq)]
|
||||||
|
pub enum Value {
|
||||||
|
/// The value is a `NULL` value.
|
||||||
|
Null,
|
||||||
|
/// The value is a signed integer.
|
||||||
|
Integer(i64),
|
||||||
|
/// The value is a floating point number.
|
||||||
|
Real(f64),
|
||||||
|
/// The value is a text string.
|
||||||
|
Text(String),
|
||||||
|
/// The value is a blob of data
|
||||||
|
Blob(Vec<u8>),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<Null> for Value {
|
||||||
|
fn from(_: Null) -> Value {
|
||||||
|
Value::Null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<bool> for Value {
|
||||||
|
fn from(i: bool) -> Value {
|
||||||
|
Value::Integer(i as i64)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<i32> for Value {
|
||||||
|
fn from(i: i32) -> Value {
|
||||||
|
Value::Integer(i as i64)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<i64> for Value {
|
||||||
|
fn from(i: i64) -> Value {
|
||||||
|
Value::Integer(i)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<f64> for Value {
|
||||||
|
fn from(f: f64) -> Value {
|
||||||
|
Value::Real(f)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<String> for Value {
|
||||||
|
fn from(s: String) -> Value {
|
||||||
|
Value::Text(s)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<Vec<u8>> for Value {
|
||||||
|
fn from(v: Vec<u8>) -> Value {
|
||||||
|
Value::Blob(v)
|
||||||
|
}
|
||||||
|
}
|
@ -70,6 +70,18 @@ impl<'a> From<ValueRef<'a>> for Value {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl<'a> From<&'a str> for ValueRef<'a> {
|
||||||
|
fn from(s: &str) -> ValueRef {
|
||||||
|
ValueRef::Text(s)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> From<&'a [u8]> for ValueRef<'a> {
|
||||||
|
fn from(s: &[u8]) -> ValueRef {
|
||||||
|
ValueRef::Blob(s)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl<'a> From<&'a Value> for ValueRef<'a> {
|
impl<'a> From<&'a Value> for ValueRef<'a> {
|
||||||
fn from(value: &'a Value) -> ValueRef<'a> {
|
fn from(value: &'a Value) -> ValueRef<'a> {
|
||||||
match *value {
|
match *value {
|
||||||
|
Loading…
Reference in New Issue
Block a user