use std::marker::PhantomData;
use std::sync::atomic::AtomicU32;
use ::worker::{SqlStorage, SqlStorageValue};
use drizzle_core::error::DrizzleError;
use drizzle_core::traits::ToSQL;
use crate::transaction::savepoint::sync_savepoint;
#[cfg(feature = "sqlite")]
use drizzle_sqlite::{
builder::{
self, QueryBuilder, delete::DeleteBuilder, insert::InsertBuilder, select::SelectBuilder,
update::UpdateBuilder,
},
builder::{DeleteInitial, InsertInitial, SelectInitial, UpdateInitial},
traits::SQLiteTable,
values::SQLiteValue,
};
use crate::builder::sqlite::durable::sqlite_value_to_storage;
pub type TransactionBuilder<'tx, Schema, Builder, State> =
crate::transaction::sqlite::typestate::TransactionBuilder<
'tx,
Transaction<Schema>,
Schema,
Builder,
State,
>;
use crate::builder::sqlite::durable::prepared;
use drizzle_core::prepared::prepare_render;
crate::drizzle_tx_prepare_impl!();
pub struct Transaction<Schema = ()> {
conn: SqlStorage,
savepoint_depth: AtomicU32,
schema: Schema,
}
impl<Schema> std::fmt::Debug for Transaction<Schema> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("Transaction").finish()
}
}
impl<Schema> Transaction<Schema> {
pub(crate) fn new(conn: SqlStorage, schema: Schema) -> Self {
Self {
conn,
savepoint_depth: AtomicU32::new(0),
schema,
}
}
#[inline]
pub fn schema(&self) -> &Schema {
&self.schema
}
#[inline]
pub fn inner(&self) -> &SqlStorage {
&self.conn
}
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.conn
.exec(sql, None)
.map(|_| ())
.map_err(|e| DrizzleError::Other(e.to_string().into()))
},
|| f(self),
)
}
sqlite_transaction_constructors!();
pub fn execute<'q, T>(&self, query: T) -> drizzle_core::error::Result<u64>
where
T: ToSQL<'q, SQLiteValue<'q>>,
{
let cursor = exec_in_tx(&self.conn, &query)?;
let _ = cursor
.to_array::<serde::de::IgnoredAny>()
.map_err(|e| DrizzleError::Other(e.to_string().into()))?;
Ok(cursor.rows_written() as u64)
}
pub fn all<'q, T, R, C>(&self, query: T) -> drizzle_core::error::Result<C>
where
R: for<'de> serde::Deserialize<'de>,
T: ToSQL<'q, SQLiteValue<'q>>,
C: Default + Extend<R>,
{
let cursor = exec_in_tx(&self.conn, &query)?;
let rows: Vec<R> = cursor
.to_array::<R>()
.map_err(|e| DrizzleError::Other(e.to_string().into()))?;
let mut out = C::default();
out.extend(rows);
Ok(out)
}
pub fn get<'q, T, R>(&self, query: T) -> drizzle_core::error::Result<R>
where
R: for<'de> serde::Deserialize<'de>,
T: ToSQL<'q, SQLiteValue<'q>>,
{
let cursor = exec_in_tx(&self.conn, &query)?;
cursor
.to_array::<R>()
.map_err(|e| DrizzleError::Other(e.to_string().into()))?
.into_iter()
.next()
.ok_or(DrizzleError::NotFound)
}
}
fn exec_in_tx<'q, T>(
conn: &SqlStorage,
query: &T,
) -> drizzle_core::error::Result<::worker::SqlCursor>
where
T: ToSQL<'q, SQLiteValue<'q>>,
{
let sql = query.to_sql();
let (sql_str, params) = sql.build();
drizzle_core::drizzle_trace_query!(&sql_str, params.len());
let values: Vec<SqlStorageValue> = params.into_iter().map(sqlite_value_to_storage).collect();
conn.exec(&sql_str, Some(values))
.map_err(|e| DrizzleError::Other(e.to_string().into()))
}
#[cfg(feature = "durable")]
impl<'tx, 'q, Schema, State, Table, Mk, Rw, Grouped>
TransactionBuilder<'tx, Schema, QueryBuilder<'q, Schema, State, Table, Mk, Rw, Grouped>, State>
where
State: builder::ExecutableState,
{
pub fn execute(self) -> drizzle_core::error::Result<u64> {
let cursor = exec_in_tx(&self.runner.conn, &self.builder.sql)?;
let _ = cursor
.to_array::<serde::de::IgnoredAny>()
.map_err(|e| DrizzleError::Other(e.to_string().into()))?;
Ok(cursor.rows_written() as u64)
}
pub fn all<R>(self) -> drizzle_core::error::Result<Vec<R>>
where
R: for<'de> serde::Deserialize<'de>,
{
let cursor = exec_in_tx(&self.runner.conn, &self.builder.sql)?;
cursor
.to_array::<R>()
.map_err(|e| DrizzleError::Other(e.to_string().into()))
}
pub fn get<R>(self) -> drizzle_core::error::Result<R>
where
R: for<'de> serde::Deserialize<'de>,
{
let cursor = exec_in_tx(&self.runner.conn, &self.builder.sql)?;
cursor
.to_array::<R>()
.map_err(|e| DrizzleError::Other(e.to_string().into()))?
.into_iter()
.next()
.ok_or(DrizzleError::NotFound)
}
}
#[cfg(feature = "query")]
use crate::builder::sqlite::common;
#[cfg(feature = "query")]
impl<Schema> Transaction<Schema> {
pub fn query<'a, T>(&self, _table: T) -> common::DrizzleQueryBuilder<'_, 'a, &Self, Schema, T>
where
T: drizzle_core::query::QueryTable,
{
common::DrizzleQueryBuilder {
runner: self,
builder: drizzle_core::query::QueryBuilder::new(),
_schema: std::marker::PhantomData,
}
}
}
#[cfg(feature = "query")]
impl<Schema> common::RelationalPreparedDriver for &Transaction<Schema> {
type PreparedDriver = SqlStorage;
}
#[cfg(feature = "query")]
impl<'db, 'a, Schema, T, Rels, Cl>
common::DrizzleQueryBuilder<
'db,
'a,
&'db Transaction<Schema>,
Schema,
T,
Rels,
drizzle_core::query::AllColumns,
Cl,
>
{
pub fn find_many(
self,
) -> drizzle_core::error::Result<
Vec<
<Rels as drizzle_core::query::BuildRow<
<T as drizzle_core::query::QueryTable>::Select,
>>::Row,
>,
>
where
T: drizzle_core::query::QueryTable,
<T as drizzle_core::query::QueryTable>::Select: drizzle_core::query::FromJsonObject,
Rels: drizzle_core::query::BuildRow<<T as drizzle_core::query::QueryTable>::Select>
+ drizzle_core::query::RenderRelations<'a, SQLiteValue<'a>>,
<Rels as drizzle_core::query::BuildStore>::Store: drizzle_core::query::DeserializeStore,
{
crate::builder::sqlite::durable::relational_find_many(self.runner.inner(), self.builder)
}
}
#[cfg(feature = "query")]
impl<'db, 'a, Schema, T, Rels, W, Ord>
common::DrizzleQueryBuilder<
'db,
'a,
&'db Transaction<Schema>,
Schema,
T,
Rels,
drizzle_core::query::AllColumns,
drizzle_core::query::Clauses<W, Ord, drizzle_core::query::NoLimit>,
>
{
pub fn find_first(
self,
) -> drizzle_core::error::Result<
Option<
<Rels as drizzle_core::query::BuildRow<
<T as drizzle_core::query::QueryTable>::Select,
>>::Row,
>,
>
where
T: drizzle_core::query::QueryTable,
<T as drizzle_core::query::QueryTable>::Select: drizzle_core::query::FromJsonObject,
Rels: drizzle_core::query::BuildRow<<T as drizzle_core::query::QueryTable>::Select>
+ drizzle_core::query::RenderRelations<'a, SQLiteValue<'a>>,
<Rels as drizzle_core::query::BuildStore>::Store: drizzle_core::query::DeserializeStore,
{
Ok(self.limit(1).find_many()?.into_iter().next())
}
}
#[cfg(feature = "query")]
impl<'db, 'a, Schema, T, Rels, Cl>
common::DrizzleQueryBuilder<
'db,
'a,
&'db Transaction<Schema>,
Schema,
T,
Rels,
drizzle_core::query::PartialColumns,
Cl,
>
{
pub fn find_many(
self,
) -> drizzle_core::error::Result<
Vec<
<Rels as drizzle_core::query::BuildRow<
<T as drizzle_core::query::QueryTable>::PartialSelect,
>>::Row,
>,
>
where
T: drizzle_core::query::QueryTable,
<T as drizzle_core::query::QueryTable>::PartialSelect: drizzle_core::query::FromJsonObject,
Rels: drizzle_core::query::BuildRow<<T as drizzle_core::query::QueryTable>::PartialSelect>
+ drizzle_core::query::RenderRelations<'a, SQLiteValue<'a>>,
<Rels as drizzle_core::query::BuildStore>::Store: drizzle_core::query::DeserializeStore,
{
crate::builder::sqlite::durable::relational_find_many_partial(
self.runner.inner(),
self.builder,
)
}
}
#[cfg(feature = "query")]
impl<'db, 'a, Schema, T, Rels, W, Ord>
common::DrizzleQueryBuilder<
'db,
'a,
&'db Transaction<Schema>,
Schema,
T,
Rels,
drizzle_core::query::PartialColumns,
drizzle_core::query::Clauses<W, Ord, drizzle_core::query::NoLimit>,
>
{
pub fn find_first(
self,
) -> drizzle_core::error::Result<
Option<
<Rels as drizzle_core::query::BuildRow<
<T as drizzle_core::query::QueryTable>::PartialSelect,
>>::Row,
>,
>
where
T: drizzle_core::query::QueryTable,
<T as drizzle_core::query::QueryTable>::PartialSelect: drizzle_core::query::FromJsonObject,
Rels: drizzle_core::query::BuildRow<<T as drizzle_core::query::QueryTable>::PartialSelect>
+ drizzle_core::query::RenderRelations<'a, SQLiteValue<'a>>,
<Rels as drizzle_core::query::BuildStore>::Store: drizzle_core::query::DeserializeStore,
{
Ok(self.limit(1).find_many()?.into_iter().next())
}
}