use drizzle_core::error::DrizzleError;
use drizzle_core::traits::ToSQL;
#[cfg(feature = "sqlite")]
use drizzle_sqlite::builder::{DeleteInitial, InsertInitial, SelectInitial, UpdateInitial};
#[cfg(feature = "sqlite")]
use drizzle_sqlite::traits::SQLiteTable;
use rusqlite::params_from_iter;
use std::marker::PhantomData;
use std::sync::atomic::AtomicU32;
use crate::builder::sqlite::rows::Rows;
use crate::transaction::savepoint::sync_savepoint;
#[cfg(feature = "sqlite")]
use drizzle_sqlite::{
builder::{
self, QueryBuilder, delete::DeleteBuilder, insert::InsertBuilder, select::SelectBuilder,
update::UpdateBuilder,
},
connection::SQLiteTransactionType,
values::SQLiteValue,
};
pub type TransactionBuilder<'tx, 'conn, Schema, Builder, State> =
crate::transaction::sqlite::typestate::TransactionBuilder<
'tx,
Transaction<'conn, Schema>,
Schema,
Builder,
State,
>;
#[derive(Debug)]
pub struct Transaction<'conn, Schema = ()> {
tx: rusqlite::Transaction<'conn>,
tx_type: SQLiteTransactionType,
savepoint_depth: AtomicU32,
schema: Schema,
}
impl<'conn, Schema> Transaction<'conn, Schema> {
pub(crate) const fn new(
tx: rusqlite::Transaction<'conn>,
tx_type: SQLiteTransactionType,
schema: Schema,
) -> Self {
Self {
tx,
tx_type,
savepoint_depth: AtomicU32::new(0),
schema,
}
}
#[inline]
pub const fn schema(&self) -> &Schema {
&self.schema
}
#[inline]
pub const fn inner(&self) -> &rusqlite::Transaction<'conn> {
&self.tx
}
#[inline]
pub const fn tx_type(&self) -> SQLiteTransactionType {
self.tx_type
}
fn execute_raw(&self, sql: &str) -> rusqlite::Result<()> {
self.tx.execute(sql, [])?;
Ok(())
}
pub fn savepoint<F, R>(&self, f: F) -> drizzle_core::error::Result<R>
where
F: FnOnce(&Self) -> drizzle_core::error::Result<R>,
{
sync_savepoint(
&self.savepoint_depth,
|sql| self.execute_raw(sql).map_err(DrizzleError::from),
|| f(self),
)
}
sqlite_transaction_constructors!('conn);
pub fn execute<'q, T>(&self, query: T) -> rusqlite::Result<usize>
where
T: ToSQL<'q, SQLiteValue<'q>>,
{
#[cfg(feature = "profiling")]
drizzle_core::drizzle_profile_scope!("sqlite.rusqlite", "tx.execute");
let query = query.to_sql();
let (sql_str, params) = query.build();
drizzle_core::drizzle_trace_query!(&sql_str, params.len());
self.tx.execute(&sql_str, params_from_iter(params))
}
pub fn all<'q, T, R>(&self, query: T) -> drizzle_core::error::Result<Vec<R>>
where
R: for<'r> TryFrom<&'r ::rusqlite::Row<'r>>,
for<'r> <R as TryFrom<&'r ::rusqlite::Row<'r>>>::Error:
Into<drizzle_core::error::DrizzleError>,
T: ToSQL<'q, SQLiteValue<'q>>,
{
self.rows(query)?
.collect::<drizzle_core::error::Result<Vec<R>>>()
}
pub fn rows<'q, T, R>(&self, query: T) -> drizzle_core::error::Result<Rows<R>>
where
R: for<'r> TryFrom<&'r ::rusqlite::Row<'r>>,
for<'r> <R as TryFrom<&'r ::rusqlite::Row<'r>>>::Error:
Into<drizzle_core::error::DrizzleError>,
T: ToSQL<'q, SQLiteValue<'q>>,
{
#[cfg(feature = "profiling")]
drizzle_core::drizzle_profile_scope!("sqlite.rusqlite", "tx.all");
let sql = query.to_sql();
let (sql_str, params) = sql.build();
drizzle_core::drizzle_trace_query!(&sql_str, params.len());
let mut stmt = self.tx.prepare(&sql_str)?;
let mut rows = stmt.query_and_then(params_from_iter(params), |row| {
R::try_from(row).map_err(Into::into)
})?;
let (lower, _) = rows.size_hint();
let mut results = Vec::with_capacity(lower);
for row in rows {
results.push(row?);
}
Ok(Rows::new(results))
}
pub fn get<'q, T, R>(&self, query: T) -> drizzle_core::error::Result<R>
where
R: for<'r> TryFrom<&'r rusqlite::Row<'r>>,
for<'r> <R as TryFrom<&'r rusqlite::Row<'r>>>::Error:
Into<drizzle_core::error::DrizzleError>,
T: ToSQL<'q, SQLiteValue<'q>>,
{
#[cfg(feature = "profiling")]
drizzle_core::drizzle_profile_scope!("sqlite.rusqlite", "tx.get");
let sql = query.to_sql();
let (sql_str, params) = sql.build();
drizzle_core::drizzle_trace_query!(&sql_str, params.len());
let mut stmt = self.tx.prepare(&sql_str)?;
stmt.query_row(params_from_iter(params), |row| {
Ok(R::try_from(row).map_err(Into::into))
})?
}
pub fn commit(self) -> rusqlite::Result<()> {
self.tx.commit()
}
pub fn rollback(self) -> rusqlite::Result<()> {
self.tx.rollback()
}
}
#[cfg(feature = "rusqlite")]
impl<'tx, 'q, S, Schema, State, Table, Mk, Rw, Grouped>
TransactionBuilder<'tx, '_, S, QueryBuilder<'q, Schema, State, Table, Mk, Rw, Grouped>, State>
where
State: builder::ExecutableState,
{
pub fn execute(self) -> drizzle_core::error::Result<usize> {
#[cfg(feature = "profiling")]
drizzle_core::drizzle_profile_scope!("sqlite.rusqlite", "tx_builder.execute");
let (sql_str, params) = self.builder.sql.build();
drizzle_core::drizzle_trace_query!(&sql_str, params.len());
Ok(self.runner.tx.execute(&sql_str, params_from_iter(params))?)
}
pub fn all<R, Proof, AggProof>(self) -> drizzle_core::error::Result<Vec<R>>
where
for<'r> Mk: drizzle_core::row::DecodeSelectedRef<&'r ::rusqlite::Row<'r>, R>
+ drizzle_core::row::MarkerScopeValidFor<Proof>
+ drizzle_core::row::StrictDecodeMarker
+ drizzle_core::row::MarkerColumnCountValid<::rusqlite::Row<'r>, Rw, R>,
Mk: drizzle_core::row::MarkerAggValidFor<Grouped, AggProof>,
{
#[cfg(feature = "profiling")]
drizzle_core::drizzle_profile_scope!("sqlite.rusqlite", "tx_builder.all");
let (sql_str, params) = self.builder.sql.build();
drizzle_core::drizzle_trace_query!(&sql_str, params.len());
let mut stmt = self.runner.tx.prepare(&sql_str)?;
let mut raw_rows = stmt.query(params_from_iter(params))?;
let mut decoded = Vec::new();
while let Some(row) = raw_rows.next()? {
decoded.push(<Mk as drizzle_core::row::DecodeSelectedRef<
&::rusqlite::Row<'_>,
R,
>>::decode(row)?);
}
Ok(decoded)
}
pub fn rows(self) -> drizzle_core::error::Result<Rows<Rw>>
where
Rw: for<'r> TryFrom<&'r ::rusqlite::Row<'r>>,
for<'r> <Rw as TryFrom<&'r ::rusqlite::Row<'r>>>::Error:
Into<drizzle_core::error::DrizzleError>,
{
#[cfg(feature = "profiling")]
drizzle_core::drizzle_profile_scope!("sqlite.rusqlite", "tx_builder.rows");
let (sql_str, params) = self.builder.sql.build();
drizzle_core::drizzle_trace_query!(&sql_str, params.len());
let mut stmt = self.runner.tx.prepare(&sql_str)?;
let mut rows = stmt.query_and_then(params_from_iter(params), |row| {
Rw::try_from(row).map_err(Into::into)
})?;
let (lower, _) = rows.size_hint();
let mut results = Vec::with_capacity(lower);
for row in rows {
results.push(row?);
}
Ok(Rows::new(results))
}
pub fn get<R, Proof, AggProof>(self) -> drizzle_core::error::Result<R>
where
for<'r> Mk: drizzle_core::row::DecodeSelectedRef<&'r ::rusqlite::Row<'r>, R>
+ drizzle_core::row::MarkerScopeValidFor<Proof>
+ drizzle_core::row::StrictDecodeMarker
+ drizzle_core::row::MarkerColumnCountValid<::rusqlite::Row<'r>, Rw, R>,
Mk: drizzle_core::row::MarkerAggValidFor<Grouped, AggProof>,
{
#[cfg(feature = "profiling")]
drizzle_core::drizzle_profile_scope!("sqlite.rusqlite", "tx_builder.get");
let (sql_str, params) = self.builder.sql.build();
drizzle_core::drizzle_trace_query!(&sql_str, params.len());
let mut stmt = self.runner.tx.prepare(&sql_str)?;
stmt.query_row(params_from_iter(params), |row| {
Ok(<Mk as drizzle_core::row::DecodeSelectedRef<
&::rusqlite::Row<'_>,
R,
>>::decode(row))
})?
}
}