use crate::database_capability::{
DatabaseTarget, GeneratedOwnedQuery, GeneratedOwnedWrite, QueryFailure,
};
use saddle_db::internal::{OwnedQueryProof, OwnedWriteProof, ReservedDatabaseTransactionSession};
pub use saddle_db::internal::ReservedTransactionBody;
pub type Cancel = tokio::sync::futures::OwnedNotified;
#[cfg(feature = "cf-transaction-construction")]
pub fn target(
process: std::sync::Arc<saddle_db::internal::StartupManagedDatabaseProcessCapability>,
) -> DatabaseTarget {
DatabaseTarget::for_construction(process)
}
pub type Session<'a, 'b, 'c> =
ReservedDatabaseTransactionSession<'a, 'b, 'c, tokio::sync::futures::OwnedNotified>;
#[cfg(feature = "cf-transaction-construction")]
pub async fn submit<B: ReservedTransactionBody<Cancel> + Send>(
process: &saddle_db::internal::StartupManagedDatabaseProcessCapability,
owner: &mut Option<saddle_runtime::profusegw::ProfuseGwSerialScope<Cancel>>,
context: &saddle_runtime::request_task::reserved::ReservedTaskContext,
isolation: crate::database::TransactionIsolation,
body: B,
) -> Result<
saddle_db::internal::ReservedDatabaseCompletion<
Cancel,
saddle_db::internal::ScopeTransactionResult<B::Value, B::Error>,
>,
saddle_runtime::profusegw::ProfuseGwReservedScopeFailure,
> {
process
.transaction_scope_reserved(owner, context, isolation, body)
.await
}
pub fn body_layout<B: ReservedTransactionBody<Cancel> + 'static>() -> std::alloc::Layout {
fn returned<I, O>(_: impl FnOnce(I) -> O) -> std::alloc::Layout {
std::alloc::Layout::new::<O>()
}
returned(
|(body, session): (B, &'static mut Session<'static, 'static, 'static>)| {
body.execute(session)
},
)
}
pub async fn optional<O: GeneratedOwnedQuery>(
session: &mut Session<'_, '_, '_>,
target: &DatabaseTarget,
parameters: O::NamedParameters,
) -> Result<Option<O::NamedRow>, QueryFailure<O::NamedParameters>> {
if let Err(error) = O::validate_parameters(¶meters, target) {
return Err(QueryFailure::Target { parameters, error });
}
let proof = OwnedQueryProof::<O>::bind().map_err(QueryFailure::Database)?;
session
.owned_optional(proof.invocation(O::parameters(parameters)))
.await
.map(|row| row.map(From::from))
.map_err(crate::database_capability::named_failure::<O>)
}
pub async fn write<O: GeneratedOwnedWrite>(
session: &mut Session<'_, '_, '_>,
target: &DatabaseTarget,
parameters: O::NamedParameters,
) -> Result<crate::database::ManagedWriteResult, QueryFailure<O::NamedParameters>> {
if let Err(error) = O::validate_parameters(¶meters, target) {
return Err(QueryFailure::Target { parameters, error });
}
let proof = OwnedWriteProof::<O>::bind().map_err(QueryFailure::Database)?;
session
.owned_write(proof.invocation(O::parameters(parameters)))
.await
.map_err(crate::database_capability::named_write_failure::<O>)
}
pub async fn rows<O: GeneratedOwnedQuery>(
session: &mut Session<'_, '_, '_>,
target: &DatabaseTarget,
parameters: O::NamedParameters,
) -> Result<crate::database::Rows<O::Row, O::NamedRow>, QueryFailure<O::NamedParameters>> {
if let Err(error) = O::validate_parameters(¶meters, target) {
return Err(QueryFailure::Target { parameters, error });
}
let proof = OwnedQueryProof::<O>::bind().map_err(QueryFailure::Database)?;
session
.owned_rows(proof.invocation(O::parameters(parameters)))
.await
.map(crate::database_rows::Rows::from_database)
.map_err(crate::database_capability::named_failure::<O>)
}
pub async fn probe<O: GeneratedOwnedQuery>(
session: &mut Session<'_, '_, '_>,
target: &DatabaseTarget,
parameters: O::NamedParameters,
) -> Result<crate::database::MatchCardinality<O::NamedRow>, QueryFailure<O::NamedParameters>> {
if let Err(error) = O::validate_parameters(¶meters, target) {
return Err(QueryFailure::Target { parameters, error });
}
let proof = OwnedQueryProof::<O>::bind().map_err(QueryFailure::Database)?;
session
.owned_probe(proof.invocation(O::parameters(parameters)))
.await
.map(|row| match row {
saddle_db::internal::OwnedDbProbe::Zero => crate::database::MatchCardinality::Zero,
saddle_db::internal::OwnedDbProbe::One(row) => {
crate::database::MatchCardinality::One(row.into())
}
saddle_db::internal::OwnedDbProbe::Many => crate::database::MatchCardinality::Many,
})
.map_err(crate::database_capability::named_failure::<O>)
}
#[macro_export]
macro_rules! transaction_logic {
($vis:vis fn $name:ident ($tx:ident : $route:ty; $($arg:ident : $ty:ty),* $(,)?) -> ($value:ty, $error:ty) $body:block) => {
$vis fn $name($($arg: $ty),*) -> impl __saddle_cf::Logic<$route, Value=$value, Error=$error> {
struct Body { $($arg: $ty),* }
impl __saddle_cf::Logic<$route> for Body {
type Value = $value;
type Error = $error;
async fn execute(self, $tx: &mut __saddle_cf::Transaction<'_, '_, '_, '_, $route>)
-> Result<$value, $crate::database::TransactionAbort<$error>> {
let Self { $($arg),* } = self;
$body
}
}
Body { $($arg),* }
}
};
}
#[doc(hidden)]
#[macro_export]
macro_rules! __saddle_cf_steps {
($route:ty, page, $name:ident, $op:ty) => {};
($route:ty, locked_rows, $name:ident, $op:ty) => {
$crate::__saddle_cf_steps!($route, rows, $name, $op);
};
($route:ty, locked_probe, $name:ident, $op:ty) => {
$crate::__saddle_cf_steps!($route, probe, $name, $op);
};
($route:ty, rows, $name:ident, $op:ty) => {
impl Transaction<'_, '_, '_, '_, $route> {
pub async fn $name(
&mut self,
p: <$op as $crate::__private::GeneratedOwnedQuery>::NamedParameters,
) -> Result<
$crate::database::Rows<
<$op as $crate::__private::StaticOwnedQuery>::Row,
<$op as $crate::__private::GeneratedOwnedQuery>::NamedRow,
>,
$crate::database::QueryFailure<
<$op as $crate::__private::GeneratedOwnedQuery>::NamedParameters,
>,
> {
$crate::transaction_construction::rows::<$op>(self.session, self.target, p).await
}
}
};
($route:ty, probe, $name:ident, $op:ty) => {
impl Transaction<'_, '_, '_, '_, $route> {
pub async fn $name(
&mut self,
p: <$op as $crate::__private::GeneratedOwnedQuery>::NamedParameters,
) -> Result<
$crate::database::MatchCardinality<
<$op as $crate::__private::GeneratedOwnedQuery>::NamedRow,
>,
$crate::database::QueryFailure<
<$op as $crate::__private::GeneratedOwnedQuery>::NamedParameters,
>,
> {
$crate::transaction_construction::probe::<$op>(self.session, self.target, p).await
}
}
};
($route:ty, query_optional, $name:ident, $op:ty) => {
impl Transaction<'_, '_, '_, '_, $route> {
pub async fn $name(
&mut self,
p: <$op as $crate::__private::GeneratedNamedQuery>::NamedParameters,
) -> Result<
Option<<$op as $crate::__private::GeneratedNamedQuery>::NamedRow>,
$crate::database::DatabaseFailure,
> {
let proof = $crate::__private::QueryOptionalOperationProof::<$op>::bind()
.map_err(|_| $crate::database::DatabaseFailure::Mapping)?;
let row = self
.session
.query(proof.invocation(
<$op as $crate::__private::GeneratedNamedQuery>::parameters(p),
))
.await?;
Ok(<$op as $crate::__private::GeneratedNamedQuery>::row(row))
}
}
};
($route:ty, optional, $name:ident, $op:ty) => {
impl Transaction<'_, '_, '_, '_, $route> {
pub async fn $name(
&mut self,
p: <$op as $crate::__private::GeneratedOwnedQuery>::NamedParameters,
) -> Result<
Option<<$op as $crate::__private::GeneratedOwnedQuery>::NamedRow>,
$crate::database::QueryFailure<
<$op as $crate::__private::GeneratedOwnedQuery>::NamedParameters,
>,
> {
$crate::transaction_construction::optional::<$op>(self.session, self.target, p)
.await
}
}
};
($route:ty, write, $name:ident, $op:ty) => {
impl Transaction<'_, '_, '_, '_, $route> {
pub async fn $name(
&mut self,
p: <$op as $crate::__private::GeneratedOwnedWrite>::NamedParameters,
) -> Result<
$crate::database::ManagedWriteResult,
$crate::database::QueryFailure<
<$op as $crate::__private::GeneratedOwnedWrite>::NamedParameters,
>,
> {
$crate::transaction_construction::write::<$op>(self.session, self.target, p).await
}
}
};
($route:ty, $kind:ident, $name:ident, $op:ty) => {
compile_error!("unsupported transaction step");
};
}
#[doc(hidden)]
#[macro_export]
macro_rules! __saddle_cf_route_steps {
($route:ty, transaction, $name:ident, {$($kind:ident $method:ident ($op:ty);)+}) => {
$($crate::__saddle_cf_steps!($route, $kind, $method, $op);)+
};
($route:ty, operations, $name:ident, {$($kind:ident $method:ident ($op:ty);)+}) => {
$($crate::__saddle_cf_steps!($route, $kind, $method, $op);)+
};
($route:ty, $($other:tt)*) => {};
}
#[doc(hidden)]
#[macro_export]
macro_rules! __saddle_cf_route_prepare {
($cap:ident, $route:ty, transaction, $method:ident, $spec:tt) => {
impl $cap<$route> {
pub fn prepare_transaction<B: __saddle_cf::Logic<$route>>(
&self,
body: B,
) -> __saddle_cf::Prepared<$route, B> {
__saddle_cf::Prepared {
body,
target: self
.__database
.as_ref()
.expect("declared transaction")
.target(),
route: ::core::marker::PhantomData,
}
}
}
};
($cap:ident, $route:ty, $($other:tt)*) => {};
}
#[doc(hidden)]
#[macro_export]
macro_rules! __saddle_cf_application {
(capability $cap:ident; modules {$($module:ident {$($mk:ident $mm:ident ($mo:ty);)+})*} routes {$($route:ident {$($kind:ident $method:ident $spec:tt)?})+}) => {
#[doc(hidden)]
pub mod __saddle_cf {
use super::*;
pub struct Transaction<'t,'s,'r,'l,R> {
session: &'t mut $crate::transaction_construction::Session<'s,'r,'l>,
target: &'t $crate::__private::DatabaseTarget,
route: ::core::marker::PhantomData<fn()->R>,
}
pub trait Logic<R>: Send + 'static {
type Value: Send + 'static;
type Error: Send + 'static;
fn execute(self, tx: &mut Transaction<'_,'_,'_,'_,R>) -> impl ::core::future::Future<Output=Result<Self::Value,$crate::database::TransactionAbort<Self::Error>>> + Send;
}
pub struct Prepared<R,B> {
pub(super) body: B,
pub(super) target: $crate::__private::DatabaseTarget,
pub(super) route: ::core::marker::PhantomData<fn()->R>,
}
pub fn body_layout<R:'static,B:Logic<R>>(_: &B)->::std::alloc::Layout {
$crate::transaction_construction::body_layout::<Prepared<R,B>>()
}
impl<R,B:Logic<R>> $crate::transaction_construction::ReservedTransactionBody<$crate::transaction_construction::Cancel> for Prepared<R,B> {
type Value=B::Value;
type Error=B::Error;
async fn execute(self, session:&mut $crate::transaction_construction::Session<'_,'_,'_>) -> Result<Self::Value,$crate::database::TransactionAbort<Self::Error>> {
let mut tx=Transaction { session, target:&self.target, route: ::core::marker::PhantomData };
self.body.execute(&mut tx).await
}
}
$($($crate::__saddle_cf_steps!($module,$mk,$mm,$mo);)+)*
$($crate::__saddle_cf_route_steps!($route,$($kind,$method,$spec)?);)+
}
};
}