use futures_util::future::BoxFuture;
use futures_util::stream::{BoxStream, TryStreamExt as _};
use super::atomic::{self, Gate};
use super::{Dialect, ExecError, PoolTx};
use crate::core::SqlValue;
use crate::sql::{DeletePlan, Pool};
#[non_exhaustive]
pub enum PoolConn {
#[cfg(feature = "postgres")]
Postgres(sqlx::pool::PoolConnection<sqlx::Postgres>),
#[cfg(feature = "mysql")]
Mysql(sqlx::pool::PoolConnection<sqlx::MySql>),
#[cfg(feature = "sqlite")]
Sqlite(sqlx::pool::PoolConnection<sqlx::Sqlite>),
}
impl Pool {
pub async fn acquire(&self) -> Result<PoolConn, ExecError> {
Ok(match self {
#[cfg(feature = "postgres")]
Pool::Postgres(p) => PoolConn::Postgres(p.acquire().await?),
#[cfg(feature = "mysql")]
Pool::Mysql(p) => PoolConn::Mysql(p.acquire().await?),
#[cfg(feature = "sqlite")]
Pool::Sqlite(p) => PoolConn::Sqlite(p.acquire().await?),
})
}
}
impl PoolConn {
#[must_use]
pub fn dialect(&self) -> &'static dyn Dialect {
match self {
#[cfg(feature = "postgres")]
PoolConn::Postgres(_) => crate::sql::postgres::DIALECT,
#[cfg(feature = "mysql")]
PoolConn::Mysql(_) => crate::sql::mysql::DIALECT,
#[cfg(feature = "sqlite")]
PoolConn::Sqlite(_) => crate::sql::sqlite::DIALECT,
}
}
pub fn close_on_drop(&mut self) {
match self {
#[cfg(feature = "postgres")]
PoolConn::Postgres(c) => c.close_on_drop(),
#[cfg(feature = "mysql")]
PoolConn::Mysql(c) => c.close_on_drop(),
#[cfg(feature = "sqlite")]
PoolConn::Sqlite(c) => c.close_on_drop(),
}
}
pub async fn begin(&mut self) -> Result<PoolTx<'_>, ExecError> {
use sqlx::Connection as _;
Ok(match self {
#[cfg(feature = "postgres")]
PoolConn::Postgres(c) => PoolTx::Postgres((**c).begin().await?),
#[cfg(feature = "mysql")]
PoolConn::Mysql(c) => PoolTx::Mysql((**c).begin().await?),
#[cfg(feature = "sqlite")]
PoolConn::Sqlite(c) => PoolTx::Sqlite((**c).begin().await?),
})
}
}
#[doc(hidden)]
pub enum RowRef<'r> {
#[cfg(feature = "postgres")]
Postgres(&'r sqlx::postgres::PgRow),
#[cfg(feature = "mysql")]
Mysql(&'r sqlx::mysql::MySqlRow),
#[cfg(feature = "sqlite")]
Sqlite(&'r sqlx::sqlite::SqliteRow),
}
async fn drain<R, U>(
mut rows: BoxStream<'_, Result<R, sqlx::Error>>,
mut decode: impl FnMut(&R) -> Result<U, sqlx::Error>,
) -> Result<Vec<U>, sqlx::Error> {
let mut out = Vec::new();
while let Some(row) = rows.try_next().await? {
out.push(decode(&row)?);
}
Ok(out)
}
#[doc(hidden)]
pub struct Guard(Gate);
impl Guard {
pub(crate) fn check<T>(&self, r: Result<T, ExecError>) -> Result<T, ExecError> {
self.0.check(r)
}
}
pub trait SqlExecutor<'c>: sealed::Sealed<'c> + Send + Sized {
#[doc(hidden)]
fn dialect(&self) -> &'static dyn Dialect;
#[doc(hidden)]
fn guard(&self) -> Result<Guard, ExecError> {
Ok(Guard(Gate::OPEN))
}
#[doc(hidden)]
fn fetch_rows<'e, 'q: 'e, U, F>(
self,
sql: &'q str,
params: Vec<SqlValue>,
decode: F,
) -> BoxFuture<'e, Result<Vec<U>, sqlx::Error>>
where
'c: 'e,
U: Send + 'e,
F: FnMut(RowRef<'_>) -> Result<U, sqlx::Error> + Send + 'e;
#[doc(hidden)]
fn execute_rows<'e, 'q: 'e>(
self,
sql: &'q str,
params: Vec<SqlValue>,
) -> BoxFuture<'e, Result<u64, sqlx::Error>>
where
'c: 'e;
#[doc(hidden)]
fn run_delete_plan<'e>(self, plan: DeletePlan) -> BoxFuture<'e, Result<u64, ExecError>>
where
'c: 'e,
{
let _ = plan;
let dialect = self.dialect().name();
Box::pin(async move {
Err(ExecError::Sql(
crate::sql::SqlError::OperatorNotSupportedInDialect {
op: "multi-statement DELETE",
dialect,
},
))
})
}
}
mod sealed {
pub trait Sealed<'c> {}
}
#[cfg(feature = "postgres")]
fn pg_rows<'c: 'e, 'e, 'q: 'e, E, U, F>(
exec: E,
sql: &'q str,
params: Vec<SqlValue>,
mut decode: F,
) -> BoxFuture<'e, Result<Vec<U>, sqlx::Error>>
where
E: sqlx::Executor<'c, Database = sqlx::Postgres>,
U: Send + 'e,
F: FnMut(RowRef<'_>) -> Result<U, sqlx::Error> + Send + 'e,
{
let rows = exec.fetch(params.into_iter().fold(sqlx::query(sql), super::bind_query));
Box::pin(drain(rows, move |r| decode(RowRef::Postgres(r))))
}
#[cfg(feature = "mysql")]
fn my_rows<'c: 'e, 'e, 'q: 'e, E, U, F>(
exec: E,
sql: &'q str,
params: Vec<SqlValue>,
mut decode: F,
) -> BoxFuture<'e, Result<Vec<U>, sqlx::Error>>
where
E: sqlx::Executor<'c, Database = sqlx::MySql>,
U: Send + 'e,
F: FnMut(RowRef<'_>) -> Result<U, sqlx::Error> + Send + 'e,
{
let rows = exec.fetch(
params
.into_iter()
.fold(sqlx::query(sql), super::bind_query_my),
);
Box::pin(drain(rows, move |r| decode(RowRef::Mysql(r))))
}
#[cfg(feature = "sqlite")]
fn sq_rows<'c: 'e, 'e, 'q: 'e, E, U, F>(
exec: E,
sql: &'q str,
params: Vec<SqlValue>,
mut decode: F,
) -> BoxFuture<'e, Result<Vec<U>, sqlx::Error>>
where
E: sqlx::Executor<'c, Database = sqlx::Sqlite>,
U: Send + 'e,
F: FnMut(RowRef<'_>) -> Result<U, sqlx::Error> + Send + 'e,
{
let q = params
.into_iter()
.fold(sqlx::query(sql), super::bind_query_sqlite);
let rows = exec.fetch(q);
Box::pin(drain(rows, move |r| decode(RowRef::Sqlite(r))))
}
#[cfg(feature = "postgres")]
fn pg_exec<'c: 'e, 'e, 'q: 'e, E>(
exec: E,
sql: &'q str,
params: Vec<SqlValue>,
) -> BoxFuture<'e, Result<u64, sqlx::Error>>
where
E: sqlx::Executor<'c, Database = sqlx::Postgres>,
{
let q = params.into_iter().fold(sqlx::query(sql), super::bind_query);
let done = exec.execute(q);
Box::pin(async move { Ok(done.await?.rows_affected()) })
}
#[cfg(feature = "mysql")]
fn my_exec<'c: 'e, 'e, 'q: 'e, E>(
exec: E,
sql: &'q str,
params: Vec<SqlValue>,
) -> BoxFuture<'e, Result<u64, sqlx::Error>>
where
E: sqlx::Executor<'c, Database = sqlx::MySql>,
{
let q = params
.into_iter()
.fold(sqlx::query(sql), super::bind_query_my);
let done = exec.execute(q);
Box::pin(async move { Ok(done.await?.rows_affected()) })
}
#[cfg(feature = "sqlite")]
fn sq_exec<'c: 'e, 'e, 'q: 'e, E>(
exec: E,
sql: &'q str,
params: Vec<SqlValue>,
) -> BoxFuture<'e, Result<u64, sqlx::Error>>
where
E: sqlx::Executor<'c, Database = sqlx::Sqlite>,
{
let q = params
.into_iter()
.fold(sqlx::query(sql), super::bind_query_sqlite);
let done = exec.execute(q);
Box::pin(async move { Ok(done.await?.rows_affected()) })
}
impl<'c> sealed::Sealed<'c> for &'c Pool {}
impl<'c> SqlExecutor<'c> for &'c Pool {
fn dialect(&self) -> &'static dyn Dialect {
Pool::dialect(self)
}
fn fetch_rows<'e, 'q: 'e, U, F>(
self,
sql: &'q str,
params: Vec<SqlValue>,
decode: F,
) -> BoxFuture<'e, Result<Vec<U>, sqlx::Error>>
where
'c: 'e,
U: Send + 'e,
F: FnMut(RowRef<'_>) -> Result<U, sqlx::Error> + Send + 'e,
{
match self {
#[cfg(feature = "postgres")]
Pool::Postgres(p) => pg_rows(p, sql, params, decode),
#[cfg(feature = "mysql")]
Pool::Mysql(p) => my_rows(p, sql, params, decode),
#[cfg(feature = "sqlite")]
Pool::Sqlite(p) => sq_rows(p, sql, params, decode),
}
}
fn execute_rows<'e, 'q: 'e>(
self,
sql: &'q str,
params: Vec<SqlValue>,
) -> BoxFuture<'e, Result<u64, sqlx::Error>>
where
'c: 'e,
{
match self {
#[cfg(feature = "postgres")]
Pool::Postgres(p) => pg_exec(p, sql, params),
#[cfg(feature = "mysql")]
Pool::Mysql(p) => my_exec(p, sql, params),
#[cfg(feature = "sqlite")]
Pool::Sqlite(p) => sq_exec(p, sql, params),
}
}
fn run_delete_plan<'e>(self, plan: DeletePlan) -> BoxFuture<'e, Result<u64, ExecError>>
where
'c: 'e,
{
Box::pin(super::delete_plan_pool(self, plan))
}
}
impl<'c> sealed::Sealed<'c> for &'c mut PoolConn {}
impl<'c> SqlExecutor<'c> for &'c mut PoolConn {
fn dialect(&self) -> &'static dyn Dialect {
PoolConn::dialect(self)
}
fn fetch_rows<'e, 'q: 'e, U, F>(
self,
sql: &'q str,
params: Vec<SqlValue>,
decode: F,
) -> BoxFuture<'e, Result<Vec<U>, sqlx::Error>>
where
'c: 'e,
U: Send + 'e,
F: FnMut(RowRef<'_>) -> Result<U, sqlx::Error> + Send + 'e,
{
match self {
#[cfg(feature = "postgres")]
PoolConn::Postgres(c) => pg_rows(&mut **c, sql, params, decode),
#[cfg(feature = "mysql")]
PoolConn::Mysql(c) => my_rows(&mut **c, sql, params, decode),
#[cfg(feature = "sqlite")]
PoolConn::Sqlite(c) => sq_rows(&mut **c, sql, params, decode),
}
}
fn execute_rows<'e, 'q: 'e>(
self,
sql: &'q str,
params: Vec<SqlValue>,
) -> BoxFuture<'e, Result<u64, sqlx::Error>>
where
'c: 'e,
{
match self {
#[cfg(feature = "postgres")]
PoolConn::Postgres(c) => pg_exec(&mut **c, sql, params),
#[cfg(feature = "mysql")]
PoolConn::Mysql(c) => my_exec(&mut **c, sql, params),
#[cfg(feature = "sqlite")]
PoolConn::Sqlite(c) => sq_exec(&mut **c, sql, params),
}
}
fn run_delete_plan<'e>(self, plan: DeletePlan) -> BoxFuture<'e, Result<u64, ExecError>>
where
'c: 'e,
{
Box::pin(async move {
let mut tx = self.begin().await?;
let n = super::run_delete_plan_tx(&mut tx, plan).await?;
tx.commit().await?;
Ok(n)
})
}
}
impl<'c> sealed::Sealed<'c> for &'c mut PoolTx<'_> {}
impl<'c> SqlExecutor<'c> for &'c mut PoolTx<'_> {
fn dialect(&self) -> &'static dyn Dialect {
PoolTx::dialect(self)
}
fn guard(&self) -> Result<Guard, ExecError> {
atomic::gate(self).map(Guard)
}
fn fetch_rows<'e, 'q: 'e, U, F>(
self,
sql: &'q str,
params: Vec<SqlValue>,
decode: F,
) -> BoxFuture<'e, Result<Vec<U>, sqlx::Error>>
where
'c: 'e,
U: Send + 'e,
F: FnMut(RowRef<'_>) -> Result<U, sqlx::Error> + Send + 'e,
{
match self {
#[cfg(feature = "postgres")]
PoolTx::Postgres(t) => pg_rows(&mut **t, sql, params, decode),
#[cfg(feature = "mysql")]
PoolTx::Mysql(t) => my_rows(&mut **t, sql, params, decode),
#[cfg(feature = "sqlite")]
PoolTx::Sqlite(t) => sq_rows(&mut **t, sql, params, decode),
}
}
fn execute_rows<'e, 'q: 'e>(
self,
sql: &'q str,
params: Vec<SqlValue>,
) -> BoxFuture<'e, Result<u64, sqlx::Error>>
where
'c: 'e,
{
match self {
#[cfg(feature = "postgres")]
PoolTx::Postgres(t) => pg_exec(&mut **t, sql, params),
#[cfg(feature = "mysql")]
PoolTx::Mysql(t) => my_exec(&mut **t, sql, params),
#[cfg(feature = "sqlite")]
PoolTx::Sqlite(t) => sq_exec(&mut **t, sql, params),
}
}
fn run_delete_plan<'e>(self, plan: DeletePlan) -> BoxFuture<'e, Result<u64, ExecError>>
where
'c: 'e,
{
Box::pin(super::run_delete_plan_tx(self, plan))
}
}
#[cfg(feature = "postgres")]
impl<'c, E: sqlx::Executor<'c, Database = sqlx::Postgres>> sealed::Sealed<'c> for E {}
#[cfg(feature = "postgres")]
impl<'c, E: sqlx::Executor<'c, Database = sqlx::Postgres>> SqlExecutor<'c> for E {
fn dialect(&self) -> &'static dyn Dialect {
crate::sql::postgres::DIALECT
}
fn fetch_rows<'e, 'q: 'e, U, F>(
self,
sql: &'q str,
params: Vec<SqlValue>,
decode: F,
) -> BoxFuture<'e, Result<Vec<U>, sqlx::Error>>
where
'c: 'e,
U: Send + 'e,
F: FnMut(RowRef<'_>) -> Result<U, sqlx::Error> + Send + 'e,
{
pg_rows(self, sql, params, decode)
}
fn execute_rows<'e, 'q: 'e>(
self,
sql: &'q str,
params: Vec<SqlValue>,
) -> BoxFuture<'e, Result<u64, sqlx::Error>>
where
'c: 'e,
{
pg_exec(self, sql, params)
}
}