use std::future::Future;
use futures::TryStreamExt;
use sqlx::Row as _;
use sqlx::any::AnyQueryResult;
use sqlx::{
Arguments, ColumnIndex, Database, Decode, Encode, Error, Executor, IntoArguments, SqlStr, Type,
};
use super::engine::{Claimed, Events, IdAt, undecodable};
use super::form::advisory::events::Candidates;
use super::queue::Queue;
#[cfg(feature = "any")]
mod any;
pub(crate) mod built_in;
mod insert;
pub(crate) mod notify;
#[cfg(feature = "any")]
pub use any::AnyDialect;
pub use built_in::{BuiltIn, BuiltInDialect};
pub use insert::{InsertSql, OnConnection, no_insert};
pub use notify::Notifies;
pub trait QueueDatabase: Database {
#[doc(hidden)]
fn bind_str(arguments: &mut Self::Arguments, value: &str) -> Result<(), Error>;
#[doc(hidden)]
fn bind_i64(arguments: &mut Self::Arguments, value: i64) -> Result<(), Error>;
#[doc(hidden)]
fn execute<'c>(
conn: &'c mut Self::Connection,
sql: &'static str,
arguments: Self::Arguments,
) -> impl Future<Output = Result<u64, Error>> + Send + 'c;
#[doc(hidden)]
fn execute_text<'c>(
conn: &'c mut Self::Connection,
sql: &'static str,
) -> impl Future<Output = Result<(), Error>> + Send + 'c;
#[doc(hidden)]
fn fetch_rows<'c, Row>(
conn: &'c mut Self::Connection,
sql: &'static str,
arguments: Self::Arguments,
queue: &'static Queue,
out: &'c mut Vec<Claimed<Row>>,
) -> impl Future<Output = Result<(), Error>> + Send + 'c
where
Row: Events<Self>,
Row::Id: for<'r> Decode<'r, Self> + Type<Self>;
#[doc(hidden)]
fn column<T>(row: &Self::Row, name: &str) -> Result<T, Error>
where
T: for<'r> Decode<'r, Self> + Type<Self>;
#[doc(hidden)]
fn fetch_ids<'c, Id>(
conn: &'c mut Self::Connection,
sql: &'static str,
arguments: Self::Arguments,
out: &'c mut Vec<Id>,
) -> impl Future<Output = Result<(), Error>> + Send + 'c
where
Id: for<'r> Decode<'r, Self> + Type<Self> + Send + Unpin + 'c;
#[doc(hidden)]
fn prepare<'c>(
conn: &'c mut Self::Connection,
sql: &'static str,
) -> impl Future<Output = Result<(), Error>> + Send + 'c;
#[doc(hidden)]
fn fetch_text<'c>(
conn: &'c mut Self::Connection,
sql: &'static str,
) -> impl Future<Output = Result<String, Error>> + Send + 'c;
#[doc(hidden)]
fn fetch_flag<'c>(
conn: &'c mut Self::Connection,
sql: &'static str,
arguments: Self::Arguments,
) -> impl Future<Output = Result<bool, Error>> + Send + 'c;
#[doc(hidden)]
fn fetch_candidates<'c, Id>(
conn: &'c mut Self::Connection,
sql: &'static str,
arguments: Self::Arguments,
out: &'c mut Candidates<Id>,
) -> impl Future<Output = Result<(), Error>> + Send + 'c
where
Id: for<'r> Decode<'r, Self> + Type<Self> + Send + 'c;
#[doc(hidden)]
fn fetch_found<'c>(
conn: &'c mut Self::Connection,
sql: &'static str,
arguments: Self::Arguments,
) -> impl Future<Output = Result<bool, Error>> + Send + 'c;
}
impl<DB> QueueDatabase for DB
where
DB: Database,
for<'c> &'c mut DB::Connection: Executor<'c, Database = DB>,
DB::Arguments: IntoArguments<DB>,
for<'q> &'q str: Encode<'q, DB> + Type<DB>,
for<'r> &'r str: Decode<'r, DB> + ColumnIndex<DB::Row>,
for<'a> i64: Encode<'a, DB> + Decode<'a, DB> + Type<DB>,
for<'r> String: Decode<'r, DB> + Type<DB>,
usize: ColumnIndex<DB::Row>,
DB::QueryResult: Into<AnyQueryResult>,
{
fn bind_str(arguments: &mut Self::Arguments, value: &str) -> Result<(), Error> {
arguments.add(value).map_err(Error::Encode)
}
fn bind_i64(arguments: &mut Self::Arguments, value: i64) -> Result<(), Error> {
arguments.add(value).map_err(Error::Encode)
}
async fn execute(
conn: &mut Self::Connection,
sql: &'static str,
arguments: Self::Arguments,
) -> Result<u64, Error> {
let result: AnyQueryResult = sqlx::query_with(sql, arguments).execute(conn).await?.into();
Ok(result.rows_affected())
}
async fn execute_text(conn: &mut Self::Connection, sql: &'static str) -> Result<(), Error> {
conn.execute(sql).await?;
Ok(())
}
async fn fetch_rows<'c, Row>(
conn: &'c mut Self::Connection,
sql: &'static str,
arguments: Self::Arguments,
queue: &'static Queue,
out: &'c mut Vec<Claimed<Row>>,
) -> Result<(), Error>
where
Row: Events<Self>,
Row::Id: for<'r> Decode<'r, Self> + Type<Self>,
{
let mut rows = sqlx::query_with::<Self, _>(sql, arguments).fetch(conn);
while let Some(raw) = rows.try_next().await? {
match Row::from_row(&raw) {
Ok(row) => out.push(Claimed::Row(row)),
Err(error) => {
let id = match queue.id_at {
IdAt::First => raw.try_get::<Row::Id, _>(0_usize),
IdAt::Named(name) => raw.try_get::<Row::Id, _>(name),
};
match id {
Ok(id) => out.push(Claimed::Undecodable {
id,
attempt: Row::read_attempt(&raw, queue),
error: Box::new(undecodable(error)),
}),
Err(_) => return Err(error),
}
}
}
}
Ok(())
}
fn column<T>(row: &Self::Row, name: &str) -> Result<T, Error>
where
T: for<'r> Decode<'r, Self> + Type<Self>,
{
row.try_get::<T, _>(name)
}
async fn fetch_ids<'c, Id>(
conn: &'c mut Self::Connection,
sql: &'static str,
arguments: Self::Arguments,
out: &'c mut Vec<Id>,
) -> Result<(), Error>
where
Id: for<'r> Decode<'r, Self> + Type<Self> + Send + Unpin + 'c,
{
let mut rows = sqlx::query_with::<Self, _>(sql, arguments).fetch(conn);
while let Some(row) = rows.try_next().await? {
out.push(row.try_get::<Id, _>(0_usize)?);
}
Ok(())
}
async fn prepare(conn: &mut Self::Connection, sql: &'static str) -> Result<(), Error> {
conn.prepare(SqlStr::from_static(sql)).await?;
Ok(())
}
fn fetch_text<'c>(
conn: &'c mut Self::Connection,
sql: &'static str,
) -> impl Future<Output = Result<String, Error>> + Send + 'c {
sqlx::query_scalar::<Self, String>(sql).fetch_one(conn)
}
async fn fetch_flag(
conn: &mut Self::Connection,
sql: &'static str,
arguments: Self::Arguments,
) -> Result<bool, Error> {
let flag: i64 = sqlx::query_scalar_with::<Self, i64, _>(sql, arguments)
.fetch_one(conn)
.await?;
Ok(flag != 0)
}
async fn fetch_candidates<'c, Id>(
conn: &'c mut Self::Connection,
sql: &'static str,
arguments: Self::Arguments,
out: &'c mut Candidates<Id>,
) -> Result<(), Error>
where
Id: for<'r> Decode<'r, Self> + Type<Self> + Send + 'c,
{
let mut rows = sqlx::query_with::<Self, _>(sql, arguments).fetch(conn);
while let Some(row) = rows.try_next().await? {
let key = row.try_get::<&str, _>(1_usize)?;
out.push(row.try_get::<Id, _>(0_usize)?, key);
}
Ok(())
}
async fn fetch_found(
conn: &mut Self::Connection,
sql: &'static str,
arguments: Self::Arguments,
) -> Result<bool, Error> {
let mut rows = sqlx::query_with::<Self, _>(sql, arguments).fetch(conn);
let mut found = false;
while rows.try_next().await?.is_some() {
found = true;
}
Ok(found)
}
}