use crate::database::Database;
use crate::error::{BoxDynError, Error};
use crate::sql_str::{SqlSafeStr, SqlStr};
use either::Either;
use futures_core::future::BoxFuture;
use futures_core::stream::BoxStream;
use futures_util::{FutureExt, StreamExt, TryFutureExt, TryStreamExt};
use std::{fmt::Debug, future};
pub trait Executor<'c>: Send + Debug + Sized {
type Database: Database;
fn execute<'e, 'q: 'e, E>(
self,
query: E,
) -> BoxFuture<'e, Result<<Self::Database as Database>::QueryResult, Error>>
where
'c: 'e,
E: 'q + Execute<'q, Self::Database>,
{
self.execute_many(query).try_collect().boxed()
}
fn execute_many<'e, 'q: 'e, E>(
self,
query: E,
) -> BoxStream<'e, Result<<Self::Database as Database>::QueryResult, Error>>
where
'c: 'e,
E: 'q + Execute<'q, Self::Database>,
{
self.fetch_many(query)
.try_filter_map(|step| async move {
Ok(match step {
Either::Left(rows) => Some(rows),
Either::Right(_) => None,
})
})
.boxed()
}
fn fetch<'e, 'q: 'e, E>(
self,
query: E,
) -> BoxStream<'e, Result<<Self::Database as Database>::Row, Error>>
where
'c: 'e,
E: 'q + Execute<'q, Self::Database>,
{
self.fetch_many(query)
.try_filter_map(|step| async move {
Ok(match step {
Either::Left(_) => None,
Either::Right(row) => Some(row),
})
})
.boxed()
}
fn fetch_many<'e, 'q: 'e, E>(
self,
query: E,
) -> BoxStream<
'e,
Result<
Either<<Self::Database as Database>::QueryResult, <Self::Database as Database>::Row>,
Error,
>,
>
where
'c: 'e,
E: 'q + Execute<'q, Self::Database>;
fn fetch_all<'e, 'q: 'e, E>(
self,
query: E,
) -> BoxFuture<'e, Result<Vec<<Self::Database as Database>::Row>, Error>>
where
'c: 'e,
E: 'q + Execute<'q, Self::Database>,
{
self.fetch(query).try_collect().boxed()
}
fn fetch_one<'e, 'q: 'e, E>(
self,
query: E,
) -> BoxFuture<'e, Result<<Self::Database as Database>::Row, Error>>
where
'c: 'e,
E: 'q + Execute<'q, Self::Database>,
{
self.fetch_optional(query)
.and_then(|row| {
future::ready(match row {
Some(row) => Ok(row),
None => Err(Error::RowNotFound),
})
})
.boxed()
}
fn fetch_optional<'e, 'q: 'e, E>(
self,
query: E,
) -> BoxFuture<'e, Result<Option<<Self::Database as Database>::Row>, Error>>
where
'c: 'e,
E: 'q + Execute<'q, Self::Database>;
#[inline]
fn prepare<'e>(
self,
query: SqlStr,
) -> BoxFuture<'e, Result<<Self::Database as Database>::Statement, Error>>
where
'c: 'e,
{
self.prepare_with(query, &[])
}
fn prepare_with<'e>(
self,
sql: SqlStr,
parameters: &'e [<Self::Database as Database>::TypeInfo],
) -> BoxFuture<'e, Result<<Self::Database as Database>::Statement, Error>>
where
'c: 'e;
#[doc(hidden)]
#[cfg(feature = "offline")]
fn describe<'e>(
self,
sql: SqlStr,
) -> BoxFuture<'e, Result<crate::describe::Describe<Self::Database>, Error>>
where
'c: 'e;
}
pub trait Execute<'q, DB: Database>: Send + Sized {
fn sql(self) -> SqlStr;
fn statement(&self) -> Option<&DB::Statement>;
fn take_arguments(&mut self) -> Result<Option<<DB as Database>::Arguments>, BoxDynError>;
fn persistent(&self) -> bool;
}
impl<DB: Database, T> Execute<'_, DB> for T
where
T: SqlSafeStr + Send,
{
#[inline]
fn sql(self) -> SqlStr {
self.into_sql_str()
}
#[inline]
fn statement(&self) -> Option<&DB::Statement> {
None
}
#[inline]
fn take_arguments(&mut self) -> Result<Option<<DB as Database>::Arguments>, BoxDynError> {
Ok(None)
}
#[inline]
fn persistent(&self) -> bool {
true
}
}
impl<DB: Database, T> Execute<'_, DB> for (T, Option<<DB as Database>::Arguments>)
where
T: SqlSafeStr + Send,
{
#[inline]
fn sql(self) -> SqlStr {
self.0.into_sql_str()
}
#[inline]
fn statement(&self) -> Option<&DB::Statement> {
None
}
#[inline]
fn take_arguments(&mut self) -> Result<Option<<DB as Database>::Arguments>, BoxDynError> {
Ok(self.1.take())
}
#[inline]
fn persistent(&self) -> bool {
true
}
}