//! This module contains data-types and functions to provide
//! prepared statement functionality.
#![forbid(missing_docs)]
use crate::{
bindings::{sqlite3_finalize, sqlite3_step, sqlite3_stmt},
ehandle::MinSqliteWrapperError,
operations::ColumnCapabilities,
prelude::*,
};
/// This enumeration is the list of the possible status outcomes for the
/// `execute_prepared(&mut self)` function.
#[non_exhaustive]
#[repr(i8)]
#[derive(Debug, PartialEq, Copy, Clone)]
pub enum PreparedStatementStatus {
/// Indicates that the type of error is currently not supported/handled by the library.
UnrecognizedStatus = -1,
/// Indicates that another row of output is available.
FoundRow,
/// Indicates that an operation has completed.
Done,
}
#[derive(Copy, Clone)]
/// Binded instance of the sqlite3_stmt.
pub struct SqlStatement(*mut sqlite3_stmt);
/// Provides prepared statement functionality.
impl<'a> SqlStatement {
/// Creates SqlStatement instance.
///
/// # Usage
/// let stmt_p = ptr::null_mut();
/// SqlStatement::new(stmt_p);
/// ```
#[inline]
pub(crate) fn new(statement: *mut sqlite3_stmt) -> Self {
Self(statement)
}
/// Executes the prepared statement and returns PreparedStatementStatus for data and error
/// handling.
///
/// # Usage
/// let db_path = Path::new("./example.db");
/// let db = Database::open(db_path).unwrap();
///
/// let statement = String::from(
/// "SELECT * FROM example_table WHERE ID = '15';"
/// );
///
/// let mut sql = db.prepare(statement, None::<Box<dyn FnOnce(SqlitePrimaryResult, String)>>).unwrap();
///
/// while let PreparedStatementStatus::FoundRow = sql.execute_prepared() {
/// ...
/// }
///
/// sql.kill();
/// db.close();
/// ```
#[inline]
pub fn execute_prepared(&mut self) -> PreparedStatementStatus {
match unsafe { sqlite3_step(self.0) } {
100 => PreparedStatementStatus::FoundRow,
101 => PreparedStatementStatus::Done,
_ => PreparedStatementStatus::UnrecognizedStatus,
}
}
/// Reads the column data of the rows that returns from the SQL query.
///
/// # Panics
/// - If the data type is incorrectly specified.
/// - If the column index doesn't match.
///
/// # Usage
/// ```
/// #[derive(Debug)]
/// struct Item {
/// id: i64,
/// name: String,
/// tag: String,
/// }
///
/// let db_path = Path::new("./example.db");
/// let db = Database::open(db_path).unwrap();
///
/// let statement = String::from(
/// "SELECT * FROM example_table WHERE ID = '15';"
/// );
///
/// let mut sql = db.prepare(statement, None::<Box<dyn FnOnce(SqlitePrimaryResult, String)>>).unwrap();
///
/// while let PreparedStatementStatus::FoundRow = sql.execute_prepared() {
/// println!(
/// "id = {}, name = {}, tag = {}",
/// sql.get_data::<i64>(0).unwrap(),
/// sql.get_data::<String>(1).unwrap(),
/// sql.get_data::<String>(2).unwrap(),
/// );
///
/// // OR
///
/// println!(
/// "{:?}",
/// Item {
/// id: sql.get_data(0).unwrap(),
/// name: sql.get_data(1).unwrap(),
/// tag: sql.get_data(2).unwrap(),
/// }
/// );
/// }
///
/// sql.kill();
/// db.close();
/// ```
#[inline]
pub fn get_data<T: ColumnCapabilities<'a>>(
&'a self,
i: usize,
) -> Result<T, MinSqliteWrapperError> {
ColumnCapabilities::get_data(self.0, i)
}
/// Binds the value of a parameter to a prepared statement indicator.
///
/// Supported indicator patterns:
/// - ?
/// - ?NNN
/// - :VVV
/// - @VVV
/// - $VVV
///
/// Returns `SqlitePrimaryResult:Ok` on success or an error code if anything goes wrong.
/// `SqlitePrimaryResult::Range` is returned if the parameter index is out of range.
///
/// # IMPORTANT
/// The first argument isn't index of the column. It's simply index of the
/// indicator and always starts at 1. If the first argument is given zero,
/// the function will return `SqlitePrimaryResult::Range`.
///
/// # Usage
/// ```
/// let db_path = Path::new("./example.db");
/// let db = Database::open(db_path).unwrap();
///
/// let statement = String::from(
/// "SELECT * FROM example_table WHERE ID = ;"
/// );
///
/// let mut sql = db.prepare(statement, None::<Box<dyn FnOnce(SqlitePrimaryResult, String)>>).unwrap();
///
/// let status = sql.bind_val(1, 5);
/// // You can do some checks by
/// assert_eq!(status, SqlitePrimaryResult::Ok);
/// // or
/// if status == SqlitePrimaryResult::Range {
/// panic!("Out of index on sql.bind_val!");
/// }
///
/// sql.kill();
/// db.close();
/// ```
#[inline]
pub fn bind_val<T: ColumnCapabilities<'a>>(&'a self, i: usize, val: T) -> SqlitePrimaryResult {
if i == 0 {
return SqlitePrimaryResult::Range;
}
ColumnCapabilities::bind_val(val, self.0, i)
}
/// Called to destroy prepared statement. This function must be called for
/// each prepared statement. Otherwise some resource leaks might happen.
///
/// # Usage
/// let db_path = Path::new("./example.db");
/// let db = Database::open(db_path).unwrap();
///
/// let statement = String::from(
/// "SELECT * FROM example_table WHERE ID = '15';"
/// );
///
/// let mut sql = db.prepare(statement, None::<Box<dyn FnOnce(SqlitePrimaryResult, String)>>).unwrap();
///
/// sql.kill();
/// db.close();
/// ```
#[inline]
pub fn kill(&self) -> SqlitePrimaryResult {
unsafe { SqlitePrimaryResult::from_i8(sqlite3_finalize(self.0) as i8) }
}
}