use core::{
ffi::{c_char, c_uchar, c_void},
ptr,
};
use super::statement::Statement;
use crate::{
blob::Reservation,
error::{Error, Result},
types::BindIndex,
};
use sqlite::{
SQLITE_TRANSIENT, sqlite3_bind_double, sqlite3_bind_int, sqlite3_bind_int64, sqlite3_bind_null,
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};
const ENCODING_UTF8: c_uchar = SQLITE_UTF8 as c_uchar;
#[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`])"
)]
pub trait Bind<'b> {
unsafe fn bind<'c>(self, statement: &Statement<'c>, index: BindIndex) -> Result<()>
where
'c: 'b;
}
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),
}
}
};
}
pub(crate) use bind;
impl<'b> Bind<'b> for i32 {
unsafe fn bind<'c>(self, statement: &Statement<'c>, index: BindIndex) -> Result<()>
where
'c: 'b,
{
bind! { sqlite3_bind_int(statement, index, self) }
}
}
impl<'b> Bind<'b> for i64 {
unsafe fn bind<'c>(self, statement: &Statement<'c>, index: BindIndex) -> Result<()>
where
'c: 'b,
{
bind! { sqlite3_bind_int64(statement, index, self) }
}
}
impl<'b> Bind<'b> for f64 {
unsafe fn bind<'c>(self, statement: &Statement<'c>, index: BindIndex) -> Result<()>
where
'c: 'b,
{
bind! { sqlite3_bind_double(statement, index, 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<'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_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<'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_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<'c>(self, statement: &Statement<'c>, index: BindIndex) -> Result<()>
where
'c: 'b,
{
unsafe { self.as_str().bind(statement, index) }
}
}
#[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<'c>(self, statement: &Statement<'c>, index: BindIndex) -> Result<()>
where
'c: 'b,
{
unsafe { self.as_slice().bind(statement, index) }
}
}
#[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<'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) }
}
}
impl<'b, T> Bind<'b> for Option<T>
where
T: Bind<'b>,
{
unsafe fn bind<'c>(self, statement: &Statement<'c>, index: BindIndex) -> Result<()>
where
'c: 'b,
{
if let Some(value) = self {
unsafe { value.bind(statement, index) }
} else {
unsafe { Null.bind(statement, index) }
}
}
}
pub(crate) struct Null;
impl<'b> Bind<'b> for Null {
unsafe fn bind<'c>(self, statement: &Statement<'c>, index: BindIndex) -> Result<()>
where
'c: 'b,
{
bind! { sqlite3_bind_null(statement, index,) }
}
}
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) };
}