use crate::{
FromRow, Result, Row, SqliteStr, ValueRef,
common::stack::Stack,
row_stream::RowStream,
sqlite::{
Database, DatabaseExt, SqliteHandle, Statement, StatementExt, StatementHandle, StepResult,
},
};
pub trait Execute<'s> {
fn prepare<S: SqliteStr>(self, sql: S) -> Result<StatementRef<'s>>;
}
impl<'s> Execute<'s> for &'s mut SqliteHandle {
fn prepare<S: SqliteStr>(self, sql: S) -> Result<StatementRef<'s>> {
Ok(StatementRef::Owned(StatementHandle::prepare_v2(self.as_ptr(), sql)?))
}
}
pub enum StatementRef<'a> {
Handle(*mut libsqlite3_sys::sqlite3_stmt),
Borrow(&'a StatementHandle),
Owned(StatementHandle),
}
impl Statement for StatementRef<'_> {
fn as_stmt_ptr(&self) -> *mut libsqlite3_sys::sqlite3_stmt {
match self {
StatementRef::Handle(h) => *h,
StatementRef::Borrow(s) => s.as_stmt_ptr(),
StatementRef::Owned(s) => s.as_stmt_ptr(),
}
}
}
pub fn query<'a, 's, S: SqliteStr, E: Execute<'s>>(sql: S, db: E) -> Query<'a, S, E> {
Query { db, sql, params: Stack::with_size() }
}
#[derive(Debug)]
pub struct Query<'a, S, E> {
db: E,
sql: S,
params: Stack<ValueRef<'a>,16>,
}
impl<'a, S, E> Query<'a, S, E> {
pub fn bind<V: Into<ValueRef<'a>>>(mut self, value: V) -> Self {
self.params.push(value.into());
self
}
}
impl<'s, S, E> Query<'_, S, E>
where
S: SqliteStr,
E: Execute<'s>
{
pub fn fetch_all<R: FromRow>(self) -> Result<Vec<R>> {
let stmt = self.db.prepare(self.sql)?;
for (param,idx) in self.params.into_iter().zip(1i32..) {
param.bind(idx, &stmt)?;
}
let mut rows = vec![];
while stmt.step()?.is_row() {
let row = Row::new(stmt.as_stmt_ptr());
rows.push(R::from_row(row)?);
}
stmt.clear_bindings()?;
stmt.reset()?;
Ok(rows)
}
pub fn fetch_optional<R: FromRow>(self) -> Result<Option<R>> {
let stmt = self.db.prepare(self.sql)?;
for (param,idx) in self.params.into_iter().zip(1i32..) {
param.bind(idx, &stmt)?;
}
let row = match stmt.step()? {
StepResult::Row => {
let row = Row::new(stmt.as_stmt_ptr());
Ok(Some(R::from_row(row)?))
}
StepResult::Done => Ok(None),
};
stmt.clear_bindings()?;
stmt.reset()?;
row
}
pub fn fetch(self) -> Result<RowStream<'s>> {
let stmt = self.db.prepare(self.sql)?;
for (param,idx) in self.params.into_iter().zip(1i32..) {
param.bind(idx, &stmt)?;
}
Ok(RowStream::new(stmt.as_stmt_ptr()))
}
pub fn execute(self) -> Result<i64> {
let stmt = self.db.prepare(self.sql)?;
let db = stmt.as_db_ptr();
for (param,idx) in self.params.into_iter().zip(1i32..) {
param.bind(idx, &stmt)?;
}
stmt.step()?;
stmt.clear_bindings()?;
stmt.reset()?;
Ok(db.last_insert_rowid())
}
}