mirror of
https://github.com/isar/rusqlite.git
synced 2025-02-17 05:18:13 +08:00
125 lines
4.3 KiB
Rust
125 lines
4.3 KiB
Rust
//! Code related to `sqlite3_context` common to `functions` and `vtab` modules.
|
|
|
|
use std::ffi::CStr;
|
|
use std::os::raw::{c_char, c_int, c_void};
|
|
#[cfg(feature = "array")]
|
|
use std::rc::Rc;
|
|
|
|
use ffi;
|
|
use ffi::sqlite3_context;
|
|
use ffi::sqlite3_value;
|
|
|
|
use str_to_cstring;
|
|
use types::{ToSqlOutput, ValueRef};
|
|
#[cfg(feature = "array")]
|
|
use vtab::array::{free_array, ARRAY_TYPE};
|
|
|
|
impl<'a> ValueRef<'a> {
|
|
pub(crate) unsafe fn from_value(value: *mut sqlite3_value) -> ValueRef<'a> {
|
|
use std::slice::from_raw_parts;
|
|
|
|
match ffi::sqlite3_value_type(value) {
|
|
ffi::SQLITE_NULL => ValueRef::Null,
|
|
ffi::SQLITE_INTEGER => ValueRef::Integer(ffi::sqlite3_value_int64(value)),
|
|
ffi::SQLITE_FLOAT => ValueRef::Real(ffi::sqlite3_value_double(value)),
|
|
ffi::SQLITE_TEXT => {
|
|
let text = ffi::sqlite3_value_text(value);
|
|
assert!(
|
|
!text.is_null(),
|
|
"unexpected SQLITE_TEXT value type with NULL data"
|
|
);
|
|
let s = CStr::from_ptr(text as *const c_char);
|
|
|
|
// sqlite3_value_text returns UTF8 data, so our unwrap here should be fine.
|
|
let s = s
|
|
.to_str()
|
|
.expect("sqlite3_value_text returned invalid UTF-8");
|
|
ValueRef::Text(s)
|
|
}
|
|
ffi::SQLITE_BLOB => {
|
|
let (blob, len) = (
|
|
ffi::sqlite3_value_blob(value),
|
|
ffi::sqlite3_value_bytes(value),
|
|
);
|
|
|
|
assert!(
|
|
len >= 0,
|
|
"unexpected negative return from sqlite3_value_bytes"
|
|
);
|
|
if len > 0 {
|
|
assert!(
|
|
!blob.is_null(),
|
|
"unexpected SQLITE_BLOB value type with NULL data"
|
|
);
|
|
ValueRef::Blob(from_raw_parts(blob as *const u8, len as usize))
|
|
} else {
|
|
// The return value from sqlite3_value_blob() for a zero-length BLOB
|
|
// is a NULL pointer.
|
|
ValueRef::Blob(&[])
|
|
}
|
|
}
|
|
_ => unreachable!("sqlite3_value_type returned invalid value"),
|
|
}
|
|
}
|
|
}
|
|
|
|
pub(crate) unsafe fn set_result<'a>(ctx: *mut sqlite3_context, result: &ToSqlOutput<'a>) {
|
|
let value = match *result {
|
|
ToSqlOutput::Borrowed(v) => v,
|
|
ToSqlOutput::Owned(ref v) => ValueRef::from(v),
|
|
|
|
#[cfg(feature = "blob")]
|
|
ToSqlOutput::ZeroBlob(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 {
|
|
ValueRef::Null => ffi::sqlite3_result_null(ctx),
|
|
ValueRef::Integer(i) => ffi::sqlite3_result_int64(ctx, i),
|
|
ValueRef::Real(r) => ffi::sqlite3_result_double(ctx, r),
|
|
ValueRef::Text(s) => {
|
|
let length = s.len();
|
|
if length > ::std::i32::MAX as usize {
|
|
ffi::sqlite3_result_error_toobig(ctx);
|
|
} else {
|
|
let c_str = match str_to_cstring(s) {
|
|
Ok(c_str) => c_str,
|
|
// TODO sqlite3_result_error
|
|
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(b) => {
|
|
let length = b.len();
|
|
if length > ::std::i32::MAX as usize {
|
|
ffi::sqlite3_result_error_toobig(ctx);
|
|
} else if length == 0 {
|
|
ffi::sqlite3_result_zeroblob(ctx, 0)
|
|
} else {
|
|
ffi::sqlite3_result_blob(
|
|
ctx,
|
|
b.as_ptr() as *const c_void,
|
|
length as c_int,
|
|
ffi::SQLITE_TRANSIENT(),
|
|
);
|
|
}
|
|
}
|
|
}
|
|
}
|