use super::{ffi, iterator::*, sqlite3_match_version, types::*, value::*, Connection};
pub use params::*;
use std::{
convert::{AsMut, AsRef},
ffi::{CStr, CString},
mem::MaybeUninit,
num::NonZeroI32,
ops::{Index, IndexMut},
slice, str,
};
mod params;
mod test;
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
enum QueryState {
Ready,
Active,
Finished,
}
pub struct Statement {
base: *mut ffi::sqlite3_stmt,
state: QueryState,
columns: Box<[Column]>,
}
impl Connection {
pub fn prepare_first<'a>(&self, sql: &'a str) -> Result<(Option<Statement>, &'a str)> {
let guard = self.lock();
let mut ret = MaybeUninit::uninit();
let mut rest = MaybeUninit::uninit();
Error::from_sqlite_desc(
unsafe {
sqlite3_match_version! {
3_020_000 => ffi::sqlite3_prepare_v3(
self.as_mut_ptr(),
sql.as_ptr() as _,
sql.len() as _,
0,
ret.as_mut_ptr(),
rest.as_mut_ptr(),
),
_ => ffi::sqlite3_prepare_v2(
self.as_mut_ptr(),
sql.as_ptr() as _,
sql.len() as _,
ret.as_mut_ptr(),
rest.as_mut_ptr(),
),
}
},
guard,
)?;
let stmt = unsafe { ret.assume_init() };
let stmt = if stmt.is_null() {
None
} else {
let len = unsafe { ffi::sqlite3_column_count(stmt) as usize };
let columns = (0..len).map(|i| Column::new(stmt, i)).collect();
Some(Statement {
base: stmt,
state: QueryState::Ready,
columns,
})
};
let rest = unsafe { rest.assume_init() };
let offset = rest as usize - sql.as_ptr() as usize;
let rest = unsafe { sql.get_unchecked(offset..) };
Ok((stmt, rest))
}
pub fn prepare(&self, sql: &str) -> Result<Statement> {
self.prepare_first(sql)?.0.ok_or(SQLITE_MISUSE)
}
pub fn query<P>(&self, sql: &str, params: P) -> Result<Statement>
where
P: Params,
{
let mut stmt = self.prepare(sql)?;
stmt.query(params)?;
Ok(stmt)
}
pub fn query_row<P, R, F>(&self, sql: &str, params: P, f: F) -> Result<R>
where
P: Params,
F: FnOnce(&mut QueryResult) -> Result<R>,
{
self.prepare(sql)?.query_row(params, f)
}
pub fn execute<P: Params>(&self, sql: &str, params: P) -> Result<i64> {
self.prepare(sql)?.execute(params)
}
pub fn insert<P: Params>(&self, sql: &str, params: P) -> Result<i64> {
self.prepare(sql)?.insert(params)
}
}
impl Statement {
pub unsafe fn as_ptr(&self) -> *mut ffi::sqlite3_stmt {
self.base
}
pub fn query<P: Params>(&mut self, params: P) -> Result<&mut Self> {
if self.state != QueryState::Ready {
self.reset()?;
}
params.bind_params(self)?;
Ok(self)
}
pub fn query_row<P, R, F>(&mut self, params: P, f: F) -> Result<R>
where
P: Params,
F: FnOnce(&mut QueryResult) -> Result<R>,
{
let res = self.query(params)?.next().map(|o| o.map(f));
let reset_res = self.reset();
match res {
Ok(None) => Err(SQLITE_EMPTY),
Ok(Some(r)) => {
reset_res?;
r
}
Err(e) => Err(e),
}
}
pub fn execute<P: Params>(&mut self, params: P) -> Result<i64> {
let db = unsafe { self.db() }.lock();
let res = self.query(params)?.next().map(|r| r.is_some());
let reset_res = self.reset();
match res {
Ok(false) => {
reset_res?;
Ok(unsafe {
sqlite3_match_version! {
3_037_000 => ffi::sqlite3_changes64(db.as_mut_ptr()),
_ => ffi::sqlite3_changes(db.as_mut_ptr()) as _,
}
})
}
Ok(true) => Err(SQLITE_MISUSE), Err(e) => Err(e),
}
}
pub fn insert<P: Params>(&mut self, params: P) -> Result<i64> {
let db = unsafe { self.db() }.lock();
let res = self.query(params)?.next().map(|r| r.is_some());
let reset_res = self.reset();
match res {
Ok(false) => {
reset_res?;
Ok(unsafe { ffi::sqlite3_last_insert_rowid(db.as_mut_ptr()) })
}
Ok(true) => Err(SQLITE_MISUSE), Err(e) => Err(e),
}
}
pub fn sql(&self) -> Result<&str> {
unsafe {
let ret = ffi::sqlite3_sql(self.base);
Ok(CStr::from_ptr(ret).to_str()?)
}
}
pub fn parameter_count(&self) -> i32 {
unsafe { ffi::sqlite3_bind_parameter_count(self.base) }
}
pub fn parameter_name(&self, position: i32) -> Option<&str> {
unsafe {
let ptr = ffi::sqlite3_bind_parameter_name(self.base, position);
match ptr.is_null() {
true => None,
false => Some(str::from_utf8_unchecked(CStr::from_ptr(ptr).to_bytes())),
}
}
}
pub fn parameter_position(&self, name: impl Into<Vec<u8>>) -> Option<NonZeroI32> {
CString::new(name).ok().and_then(|name| {
NonZeroI32::new(unsafe { ffi::sqlite3_bind_parameter_index(self.base, name.as_ptr()) })
})
}
pub fn column_count(&self) -> usize {
unsafe { ffi::sqlite3_column_count(self.base) as _ }
}
pub fn current_result(&self) -> Option<&QueryResult> {
match self.state {
QueryState::Active => Some(QueryResult::from_statement(self)),
_ => None,
}
}
pub fn current_result_mut(&mut self) -> Option<&mut QueryResult> {
match self.state {
QueryState::Active => Some(QueryResult::from_statement_mut(self)),
_ => None,
}
}
pub unsafe fn db<'a>(&self) -> &'a Connection {
Connection::from_ptr(ffi::sqlite3_db_handle(self.base))
}
fn reset(&mut self) -> Result<()> {
unsafe {
ffi::sqlite3_reset(self.base);
Error::from_sqlite(ffi::sqlite3_clear_bindings(self.base))?;
}
self.state = QueryState::Ready;
Ok(())
}
}
impl FallibleIteratorMut for Statement {
type Item = QueryResult;
type Error = Error;
fn next(&mut self) -> Result<Option<&mut Self::Item>> {
match self.state {
QueryState::Ready | QueryState::Active => unsafe {
let guard = self.db().lock();
let rc = ffi::sqlite3_step(self.base);
Error::from_sqlite_desc(rc, guard)?;
match rc {
ffi::SQLITE_DONE => {
self.state = QueryState::Finished;
Ok(None)
}
ffi::SQLITE_ROW => {
self.state = QueryState::Active;
Ok(Some(QueryResult::from_statement_mut(self)))
}
_ => unreachable!(),
}
},
QueryState::Finished => Ok(None),
}
}
}
impl std::fmt::Debug for Statement {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
f.debug_struct("Statement")
.field("state", &self.state)
.finish_non_exhaustive()
}
}
impl Drop for Statement {
fn drop(&mut self) {
unsafe { ffi::sqlite3_finalize(self.base) };
}
}
#[repr(transparent)]
pub struct QueryResult {
stmt: Statement,
}
impl QueryResult {
fn from_statement(stmt: &Statement) -> &Self {
unsafe { &*(stmt as *const Statement as *const Self) }
}
fn from_statement_mut(stmt: &mut Statement) -> &mut Self {
unsafe { &mut *(stmt as *mut Statement as *mut Self) }
}
pub fn len(&self) -> usize {
self.stmt.column_count()
}
pub fn is_empty(&self) -> bool {
self.len() == 0
}
}
impl Index<usize> for QueryResult {
type Output = Column;
fn index(&self, index: usize) -> &Self::Output {
&self.stmt.columns[index]
}
}
impl IndexMut<usize> for QueryResult {
fn index_mut(&mut self, index: usize) -> &mut Self::Output {
&mut self.stmt.columns[index]
}
}
impl std::fmt::Debug for QueryResult {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let mut dt = f.debug_tuple("QueryResult");
for i in 0..self.len() {
dt.field(&self[i]);
}
dt.finish()
}
}
pub struct Column {
stmt: *mut ffi::sqlite3_stmt,
position: usize,
}
impl Column {
fn new(stmt: *mut ffi::sqlite3_stmt, position: usize) -> Self {
Self { stmt, position }
}
pub fn name(&self) -> Result<&str> {
unsafe {
let ret = ffi::sqlite3_column_name(self.stmt, self.position as _);
if ret.is_null() {
Err(SQLITE_NOMEM)
} else {
Ok(CStr::from_ptr(ret).to_str()?)
}
}
}
pub fn database_name(&self) -> Result<Option<&str>> {
unsafe {
let ret = ffi::sqlite3_column_database_name(self.stmt, self.position as _);
if ret.is_null() {
Ok(None)
} else {
Ok(Some(CStr::from_ptr(ret).to_str()?))
}
}
}
pub fn table_name(&self) -> Result<Option<&str>> {
unsafe {
let ret = ffi::sqlite3_column_table_name(self.stmt, self.position as _);
if ret.is_null() {
Ok(None)
} else {
Ok(Some(CStr::from_ptr(ret).to_str()?))
}
}
}
pub fn origin_name(&self) -> Result<Option<&str>> {
unsafe {
let ret = ffi::sqlite3_column_origin_name(self.stmt, self.position as _);
if ret.is_null() {
Ok(None)
} else {
Ok(Some(CStr::from_ptr(ret).to_str()?))
}
}
}
pub fn decltype(&self) -> Result<Option<&str>> {
unsafe {
let ret = ffi::sqlite3_column_decltype(self.stmt, self.position as _);
if ret.is_null() {
Ok(None)
} else {
Ok(Some(CStr::from_ptr(ret).to_str()?))
}
}
}
}
impl AsRef<ValueRef> for Column {
fn as_ref(&self) -> &ValueRef {
unsafe { ValueRef::from_ptr(ffi::sqlite3_column_value(self.stmt, self.position as _)) }
}
}
impl AsMut<ValueRef> for Column {
fn as_mut(&mut self) -> &mut ValueRef {
unsafe { ValueRef::from_ptr(ffi::sqlite3_column_value(self.stmt, self.position as _)) }
}
}
impl FromValue for Column {
fn value_type(&self) -> ValueType {
unsafe { ValueType::from_sqlite(ffi::sqlite3_column_type(self.stmt, self.position as _)) }
}
fn get_i32(&self) -> i32 {
unsafe { ffi::sqlite3_column_int(self.stmt, self.position as _) }
}
fn get_i64(&self) -> i64 {
unsafe { ffi::sqlite3_column_int64(self.stmt, self.position as _) }
}
fn get_f64(&self) -> f64 {
unsafe { ffi::sqlite3_column_double(self.stmt, self.position as _) }
}
unsafe fn get_blob_unchecked(&self) -> &[u8] {
let len = ffi::sqlite3_column_bytes(self.stmt, self.position as _);
if len == 0 {
return &[];
}
let data = ffi::sqlite3_column_blob(self.stmt, self.position as _);
slice::from_raw_parts(data as _, len as _)
}
fn get_blob(&mut self) -> Result<&[u8]> {
unsafe {
let len = ffi::sqlite3_column_bytes(self.stmt, self.position as _);
if len == 0 {
return Ok(&[]);
}
let data = ffi::sqlite3_column_blob(self.stmt, self.position as _);
if data.is_null() {
Err(SQLITE_NOMEM)
} else {
Ok(slice::from_raw_parts(data as _, len as _))
}
}
}
}
impl std::fmt::Debug for Column {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::result::Result<(), std::fmt::Error> {
match self.value_type() {
ValueType::Integer => f.debug_tuple("Integer").field(&self.get_i64()).finish(),
ValueType::Float => f.debug_tuple("Float").field(&self.get_f64()).finish(),
ValueType::Text => f
.debug_tuple("Text")
.field(unsafe { &self.get_str_unchecked() })
.finish(),
ValueType::Blob => f
.debug_tuple("Blob")
.field(unsafe { &self.get_blob_unchecked() })
.finish(),
ValueType::Null => f.debug_tuple("Null").finish(),
}
}
}