use core::{
ffi::{c_char, c_uchar, c_void},
ptr,
};
#[cfg(feature = "functions")]
use super::func::ContextRef;
use super::{pointer::Pointee, statement::Statement};
use crate::{
blob::Reservation,
error::{Error, Result},
types::{BindIndex, Borrowed},
};
use sqlite::{
SQLITE_STATIC, SQLITE_TRANSIENT, sqlite3_bind_double, sqlite3_bind_int, sqlite3_bind_int64,
sqlite3_bind_null, sqlite3_bind_pointer, sqlite3_destructor_type,
};
#[cfg(target_pointer_width = "64")]
use sqlite::{
SQLITE_UTF8, sqlite3_bind_blob64, sqlite3_bind_text64, sqlite3_bind_zeroblob64, sqlite3_uint64,
};
#[cfg(target_pointer_width = "32")]
use sqlite::{sqlite3_bind_blob, sqlite3_bind_text, sqlite3_bind_zeroblob};
#[cfg(all(feature = "functions", target_pointer_width = "32"))]
use sqlite::{sqlite3_result_blob, sqlite3_result_text, sqlite3_result_zeroblob};
#[cfg(all(feature = "functions", target_pointer_width = "64"))]
use sqlite::{sqlite3_result_blob64, sqlite3_result_text64, sqlite3_result_zeroblob64};
#[cfg(feature = "functions")]
use sqlite::{
sqlite3_result_double, sqlite3_result_int, sqlite3_result_int64, sqlite3_result_null,
sqlite3_result_pointer,
};
#[cfg(target_pointer_width = "64")]
const ENCODING_UTF8: c_uchar = SQLITE_UTF8 as c_uchar;
#[cfg_attr(not(feature = "functions"), doc = "parameter.")]
#[cfg_attr(
feature = "functions",
doc = "parameter, or [returned][result] from a SQL function."
)]
#[cfg_attr(
target_pointer_width = "32",
doc = " - [`&str`](str) (via [`sqlite3_bind_text`])"
)]
#[cfg_attr(
target_pointer_width = "64",
doc = " - [`&str`](str) (via [`sqlite3_bind_text64`])"
)]
#[cfg_attr(
target_pointer_width = "32",
doc = " - [`&[u8]`](primitive@slice) (via [`sqlite3_bind_blob`])"
)]
#[cfg_attr(
target_pointer_width = "64",
doc = " - [`&[u8]`](primitive@slice) (via [`sqlite3_bind_blob64`])"
)]
#[cfg_attr(
target_pointer_width = "32",
doc = " - [`Reservation`] (via [`sqlite3_bind_zeroblob`])"
)]
#[cfg_attr(
target_pointer_width = "64",
doc = " - [`Reservation`] (via [`sqlite3_bind_zeroblob64`])"
)]
#[cfg_attr(
feature = "functions",
doc = "[result]: https://sqlite.org/c3ref/result_blob.html"
)]
pub trait Bind<'b> {
unsafe fn bind_parameter<'c>(self, statement: &Statement<'c>, index: BindIndex) -> Result<()>
where
'c: 'b;
#[cfg(feature = "functions")]
#[cfg_attr(docsrs, doc(cfg(feature = "functions")))]
unsafe fn bind_return<'c>(self, context: &ContextRef<'c>)
where
'b: 'c;
}
macro_rules! bind {
{ $fn:ident($stmt:expr, $index:expr, $($arg:expr),*) } => {
{
let result = unsafe { $fn($stmt.as_ptr(), $index.value(), $($arg),*) };
match Error::from_connection($stmt, result) {
None => Ok(()),
Some(err) => Err(err),
}
}
};
}
#[cfg(feature = "functions")]
macro_rules! result {
{ $fn:ident($ctx:expr, $($arg:expr),*) } => {
unsafe { $fn($ctx.as_ptr(), $($arg),*) }
};
}
pub(crate) use bind;
#[cfg(feature = "functions")]
pub(crate) use result;
impl<'b> Bind<'b> for i32 {
unsafe fn bind_parameter<'c>(self, statement: &Statement<'c>, index: BindIndex) -> Result<()>
where
'c: 'b,
{
bind! { sqlite3_bind_int(statement, index, self) }
}
#[cfg(feature = "functions")]
unsafe fn bind_return<'c>(self, context: &ContextRef<'c>)
where
'b: 'c,
{
result! { sqlite3_result_int(context, self) }
}
}
impl<'b> Bind<'b> for i64 {
unsafe fn bind_parameter<'c>(self, statement: &Statement<'c>, index: BindIndex) -> Result<()>
where
'c: 'b,
{
bind! { sqlite3_bind_int64(statement, index, self) }
}
#[cfg(feature = "functions")]
unsafe fn bind_return<'c>(self, context: &ContextRef<'c>)
where
'b: 'c,
{
result! { sqlite3_result_int64(context, self) }
}
}
impl<'b> Bind<'b> for f64 {
unsafe fn bind_parameter<'c>(self, statement: &Statement<'c>, index: BindIndex) -> Result<()>
where
'c: 'b,
{
bind! { sqlite3_bind_double(statement, index, self) }
}
#[cfg(feature = "functions")]
unsafe fn bind_return<'c>(self, context: &ContextRef<'c>)
where
'b: 'c,
{
result! { sqlite3_result_double(context, self) }
}
}
#[cfg_attr(
target_pointer_width = "32",
doc = "[Binds](Bind) a [`&str`](str) via [`sqlite3_bind_text`]."
)]
#[cfg_attr(
target_pointer_width = "64",
doc = "[Binds](Bind) a [`&str`](str) via [`sqlite3_bind_text64`]."
)]
impl<'b> Bind<'b> for &str {
unsafe fn bind_parameter<'c>(self, statement: &Statement<'c>, index: BindIndex) -> Result<()>
where
'c: 'b,
{
#[cfg(target_pointer_width = "32")]
bind! { sqlite3_bind_text(statement, index, self.as_ptr() as *const c_char, self.len() as c_int, SQLITE_TRANSIENT) }?;
#[cfg(target_pointer_width = "64")]
bind! { sqlite3_bind_text64(statement, index, self.as_ptr() as *const c_char, self.len() as sqlite3_uint64, SQLITE_TRANSIENT, ENCODING_UTF8) }?;
Ok(())
}
#[cfg(feature = "functions")]
unsafe fn bind_return<'c>(self, context: &ContextRef<'c>)
where
'b: 'c,
{
#[cfg(target_pointer_width = "32")]
result! { sqlite3_result_text(context, self.as_ptr() as *const c_char, self.len() as c_int, SQLITE_TRANSIENT) }
#[cfg(target_pointer_width = "64")]
result! { sqlite3_result_text64(context, self.as_ptr() as *const c_char, self.len() as sqlite3_uint64, SQLITE_TRANSIENT, ENCODING_UTF8) }
}
}
#[cfg_attr(
target_pointer_width = "32",
doc = "[Binds](Bind) a [`&[u8]`](primitive@slice) via [`sqlite3_bind_blob`]."
)]
#[cfg_attr(
target_pointer_width = "64",
doc = "[Binds](Bind) a [`&[u8]`](primitive@slice) via [`sqlite3_bind_blob64`]."
)]
impl<'b> Bind<'b> for &[u8] {
unsafe fn bind_parameter<'c>(self, statement: &Statement<'c>, index: BindIndex) -> Result<()>
where
'c: 'b,
{
#[cfg(target_pointer_width = "32")]
bind! { sqlite3_bind_blob(statement, index, self.as_ptr() as *const c_void, self.len() as c_int, SQLITE_TRANSIENT) }
#[cfg(target_pointer_width = "64")]
bind! { sqlite3_bind_blob64(statement, index, self.as_ptr() as *const c_void, self.len() as sqlite3_uint64, SQLITE_TRANSIENT) }
}
#[cfg(feature = "functions")]
unsafe fn bind_return<'c>(self, context: &ContextRef<'c>)
where
'b: 'c,
{
#[cfg(target_pointer_width = "32")]
result! { sqlite3_result_blob(context, self.as_ptr() as *const c_void, self.len() as c_int, SQLITE_TRANSIENT) }
#[cfg(target_pointer_width = "64")]
result! { sqlite3_result_blob64(context, self.as_ptr() as *const c_void, self.len() as sqlite3_uint64, SQLITE_TRANSIENT) }
}
}
#[cfg_attr(
target_pointer_width = "32",
doc = "[Binds](Bind) a [`String`] via [`sqlite3_bind_text`]."
)]
#[cfg_attr(
target_pointer_width = "64",
doc = "[Binds](Bind) a [`String`] via [`sqlite3_bind_text64`]."
)]
impl<'b> Bind<'b> for String {
unsafe fn bind_parameter<'c>(self, statement: &Statement<'c>, index: BindIndex) -> Result<()>
where
'c: 'b,
{
unsafe { self.as_str().bind_parameter(statement, index) }
}
#[cfg(feature = "functions")]
unsafe fn bind_return<'c>(self, context: &ContextRef<'c>)
where
'b: 'c,
{
unsafe { self.as_str().bind_return(context) }
}
}
#[cfg_attr(
target_pointer_width = "32",
doc = "[Binds](Bind) a `Vec<u8>` via [`sqlite3_bind_blob`]."
)]
#[cfg_attr(
target_pointer_width = "64",
doc = "[Binds](Bind) a `Vec<u8>` via [`sqlite3_bind_blob64`]."
)]
impl<'b> Bind<'b> for Vec<u8> {
unsafe fn bind_parameter<'c>(self, statement: &Statement<'c>, index: BindIndex) -> Result<()>
where
'c: 'b,
{
unsafe { self.as_slice().bind_parameter(statement, index) }
}
#[cfg(feature = "functions")]
unsafe fn bind_return<'c>(self, context: &ContextRef<'c>)
where
'b: 'c,
{
unsafe { self.as_slice().bind_return(context) }
}
}
#[cfg_attr(
target_pointer_width = "32",
doc = "[Binds](Bind) a [blob reservation](Reservation) via [`sqlite3_bind_zeroblob`]."
)]
#[cfg_attr(
target_pointer_width = "64",
doc = "[Binds](Bind) a [blob reservation](Reservation) via [`sqlite3_bind_zeroblob64`]."
)]
impl<'b> Bind<'b> for Reservation {
unsafe fn bind_parameter<'c>(self, statement: &Statement<'c>, index: BindIndex) -> Result<()>
where
'c: 'b,
{
#[cfg(target_pointer_width = "32")]
bind! { sqlite3_bind_zeroblob(statement, index, self.0 as c_int) }
#[cfg(target_pointer_width = "64")]
bind! { sqlite3_bind_zeroblob64(statement, index, self.len() as sqlite3_uint64) }
}
#[cfg(feature = "functions")]
unsafe fn bind_return<'c>(self, context: &ContextRef<'c>)
where
'b: 'c,
{
#[cfg(target_pointer_width = "32")]
result! { sqlite3_result_zeroblob(context, self.0 as c_int) }
#[cfg(target_pointer_width = "64")]
result! { sqlite3_result_zeroblob64(context, self.len() as sqlite3_uint64) };
}
}
impl<'b, T> Bind<'b> for Option<T>
where
T: Bind<'b>,
{
unsafe fn bind_parameter<'c>(self, statement: &Statement<'c>, index: BindIndex) -> Result<()>
where
'c: 'b,
{
if let Some(value) = self {
unsafe { value.bind_parameter(statement, index) }
} else {
unsafe { Null.bind_parameter(statement, index) }
}
}
#[cfg(feature = "functions")]
unsafe fn bind_return<'c>(self, context: &ContextRef<'c>)
where
'b: 'c,
{
if let Some(value) = self {
unsafe { value.bind_return(context) }
} else {
unsafe { Null.bind_return(context) }
}
}
}
#[cfg_attr(
target_pointer_width = "32",
doc = "[Binds](Bind) a [borrowed](Borrowed) [`str`](str) via [`sqlite3_bind_text`]."
)]
#[cfg_attr(
target_pointer_width = "64",
doc = "[Binds](Bind) a [borrowed](Borrowed) [`str`](str) via [`sqlite3_bind_text64`]."
)]
impl<'b, 'a: 'b> Bind<'b> for Borrowed<'a, str> {
unsafe fn bind_parameter<'c>(self, statement: &Statement<'c>, index: BindIndex) -> Result<()>
where
'c: 'b,
{
#[cfg(target_pointer_width = "32")]
bind! { sqlite3_bind_text(statement, index, self.as_ptr() as *const c_char, self.len() as c_int, SQLITE_STATIC) }?;
#[cfg(target_pointer_width = "64")]
bind! { sqlite3_bind_text64(statement, index, self.as_ptr() as *const c_char, self.len() as sqlite3_uint64, SQLITE_STATIC, ENCODING_UTF8) }?;
Ok(())
}
#[cfg(feature = "functions")]
unsafe fn bind_return<'c>(self, context: &ContextRef<'c>)
where
'b: 'c,
{
#[cfg(target_pointer_width = "32")]
result! { sqlite3_result_text(context, self.as_ptr() as *const c_char, self.len() as c_int, SQLITE_STATIC) }
#[cfg(target_pointer_width = "64")]
result! { sqlite3_result_text64(context, self.as_ptr() as *const c_char, self.len() as sqlite3_uint64, SQLITE_STATIC, ENCODING_UTF8) }
}
}
#[cfg_attr(
target_pointer_width = "32",
doc = "[Binds](Bind) a [borrowed](Borrowed) [`[u8]`](primitive@slice) via [`sqlite3_bind_blob`]."
)]
#[cfg_attr(
target_pointer_width = "64",
doc = "[Binds](Bind) a [borrowed](Borrowed) [`[u8]`](primitive@slice) via [`sqlite3_bind_blob64`]."
)]
impl<'b, 'a: 'b> Bind<'b> for Borrowed<'a, [u8]> {
unsafe fn bind_parameter<'c>(self, statement: &Statement<'c>, index: BindIndex) -> Result<()>
where
'c: 'b,
{
#[cfg(target_pointer_width = "32")]
bind! { sqlite3_bind_blob(statement, index, self.as_ptr() as *const c_void, self.len() as c_int, SQLITE_STATIC) }
#[cfg(target_pointer_width = "64")]
bind! { sqlite3_bind_blob64(statement, index, self.as_ptr() as *const c_void, self.len() as sqlite3_uint64, SQLITE_STATIC) }
}
#[cfg(feature = "functions")]
unsafe fn bind_return<'c>(self, context: &ContextRef<'c>)
where
'b: 'c,
{
#[cfg(target_pointer_width = "32")]
result! { sqlite3_result_blob(context, self.as_ptr() as *const c_void, self.len() as c_int, SQLITE_STATIC) }
#[cfg(target_pointer_width = "64")]
result! { sqlite3_result_blob64(context, self.as_ptr() as *const c_void, self.len() as sqlite3_uint64, SQLITE_STATIC) }
}
}
impl<'b, 'a: 'b, T: Pointee + ?Sized> Bind<'b> for &'a T {
unsafe fn bind_parameter<'c>(self, statement: &Statement<'c>, index: BindIndex) -> Result<()>
where
'c: 'b,
{
let pointer = self as *const T;
bind! { sqlite3_bind_pointer(statement, index, pointer as *mut c_void, T::TYPE.as_ptr(), SQLITE_STATIC) }
}
#[cfg(feature = "functions")]
unsafe fn bind_return<'c>(self, context: &ContextRef<'c>)
where
'b: 'c,
{
let pointer = self as *const T;
result! { sqlite3_result_pointer(context, pointer as *mut c_void, T::TYPE.as_ptr(), SQLITE_STATIC) }
}
}
impl<'b, 'a: 'b, T: Pointee + ?Sized> Bind<'b> for &'a mut T {
unsafe fn bind_parameter<'c>(self, statement: &Statement<'c>, index: BindIndex) -> Result<()>
where
'c: 'b,
{
let pointer = self as *const T;
bind! { sqlite3_bind_pointer(statement, index, pointer as *mut c_void, T::TYPE.as_ptr(), SQLITE_STATIC) }
}
#[cfg(feature = "functions")]
unsafe fn bind_return<'c>(self, context: &ContextRef<'c>)
where
'b: 'c,
{
let pointer = self as *const T;
result! { sqlite3_result_pointer(context, pointer as *mut c_void, T::TYPE.as_ptr(), SQLITE_STATIC) }
}
}
impl<'b, T: Pointee> Bind<'b> for Box<T> {
unsafe fn bind_parameter<'c>(self, statement: &Statement<'c>, index: BindIndex) -> Result<()>
where
'c: 'b,
{
let pointer = Box::into_raw(self);
let destructor = sqlite3_destructor_type::new(destroy_box::<T>);
bind! { sqlite3_bind_pointer(statement, index, pointer as *mut c_void, T::TYPE.as_ptr(), destructor) }
}
#[cfg(feature = "functions")]
unsafe fn bind_return<'c>(self, context: &ContextRef<'c>)
where
'b: 'c,
{
let pointer = Box::into_raw(self);
let destructor = sqlite3_destructor_type::new(destroy_box::<T>);
result! { sqlite3_result_pointer(context, pointer as *mut c_void, T::TYPE.as_ptr(), destructor) }
}
}
pub(crate) struct Null;
impl<'b> Bind<'b> for Null {
unsafe fn bind_parameter<'c>(self, statement: &Statement<'c>, index: BindIndex) -> Result<()>
where
'c: 'b,
{
bind! { sqlite3_bind_null(statement, index,) }
}
#[cfg(feature = "functions")]
unsafe fn bind_return<'c>(self, context: &ContextRef<'c>)
where
'b: 'c,
{
result! { sqlite3_result_null(context,) }
}
}
pub const fn destructor<T>() -> sqlite3_destructor_type {
sqlite3_destructor_type::new(destroy::<T>)
}
unsafe extern "C" fn destroy<T>(p: *mut c_void) {
unsafe { ptr::drop_in_place(p as *mut T) };
}
pub(super) unsafe extern "C" fn destroy_box<T>(p: *mut c_void) {
let _ = unsafe { Box::from_raw(p as *mut T) };
}