use std::fmt;
use std::num::NonZeroUsize;
#[cfg(any(feature = "postgres", feature = "mysql", feature = "sqlite"))]
use ruststream_sqlx_dialect as dialect;
#[cfg(any(feature = "postgres", feature = "mysql", feature = "any"))]
use ruststream_sqlx_dialect::RowLock;
use ruststream_sqlx_dialect::{
Advisory, ClaimShape, Dialect, Lease, Opening, Statement, StatementError, TableName, TableSpec,
};
#[cfg(any(
feature = "postgres",
feature = "mysql",
feature = "sqlite",
feature = "any"
))]
use ruststream_sqlx_dialect::{Opens, level};
#[cfg(feature = "any")]
use sqlx::{Any, AnyConnection};
#[cfg(feature = "mysql")]
use sqlx::{MySql, MySqlConnection};
#[cfg(feature = "postgres")]
use sqlx::{PgConnection, Postgres};
#[cfg(feature = "sqlite")]
use sqlx::{Sqlite, SqliteConnection};
use super::QueueDatabase;
#[cfg(feature = "any")]
use super::any::AnyDialect;
pub struct BuiltIn<DB: BuiltInDialect>(DB::Picked);
impl<DB: BuiltInDialect> BuiltIn<DB> {
#[cfg(any(
feature = "postgres",
feature = "mysql",
feature = "sqlite",
feature = "any"
))]
pub(crate) const fn new(picked: DB::Picked) -> Self {
Self(picked)
}
}
impl<DB: BuiltInDialect> Clone for BuiltIn<DB> {
fn clone(&self) -> Self {
*self
}
}
impl<DB: BuiltInDialect> Copy for BuiltIn<DB> {}
impl<DB: BuiltInDialect> fmt::Debug for BuiltIn<DB> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_tuple("BuiltIn").field(&self.0.name()).finish()
}
}
impl<DB: BuiltInDialect> Dialect for BuiltIn<DB> {
fn name(&self) -> &'static str {
self.0.name()
}
fn quote_into(&self, ident: &str, out: &mut String) {
self.0.quote_into(ident, out);
}
fn placeholder_into(&self, index: NonZeroUsize, out: &mut String) {
self.0.placeholder_into(index, out);
}
fn fetch(&self, spec: &TableSpec<'_>) -> Result<Statement, StatementError> {
self.0.fetch(spec)
}
fn ack(&self, spec: &TableSpec<'_>) -> Result<Statement, StatementError> {
self.0.ack(spec)
}
fn retry(&self, spec: &TableSpec<'_>) -> Result<Option<Statement>, StatementError> {
self.0.retry(spec)
}
fn retry_after(&self, spec: &TableSpec<'_>) -> Result<Statement, StatementError> {
self.0.retry_after(spec)
}
fn discard(&self, spec: &TableSpec<'_>) -> Result<Statement, StatementError> {
self.0.discard(spec)
}
fn dead_letter_group(&self, spec: &TableSpec<'_>) -> Result<Statement, StatementError> {
self.0.dead_letter_group(spec)
}
fn dead_letter_table(
&self,
spec: &TableSpec<'_>,
target: TableName<'_>,
) -> Result<Vec<Statement>, StatementError> {
self.0.dead_letter_table(spec, target)
}
fn insert(&self, spec: &TableSpec<'_>) -> Result<Statement, StatementError> {
self.0.insert(spec)
}
fn server_version(&self) -> Option<&'static str> {
self.0.server_version()
}
fn check_server(&self, spec: &TableSpec<'_>, version: &str) -> Result<(), StatementError> {
self.0.check_server(spec, version)
}
fn begin(&self, opening: Opening) -> Result<Option<&'static str>, StatementError> {
self.0.begin(opening)
}
fn savepoint(&self) -> &'static str {
self.0.savepoint()
}
fn rollback_to_savepoint(&self) -> &'static str {
self.0.rollback_to_savepoint()
}
fn fifo_guard(&self, spec: &TableSpec<'_>) -> Result<Option<Statement>, StatementError> {
self.0.fifo_guard(spec)
}
}
impl<DB: BuiltInDialect> Lease for BuiltIn<DB> {
fn lease_claim(
&self,
spec: &TableSpec<'_>,
shape: ClaimShape,
) -> Result<Statement, StatementError> {
self.0.lease_claim(spec, shape)
}
fn extend(&self, spec: &TableSpec<'_>) -> Result<Statement, StatementError> {
self.0.extend(spec)
}
fn stamp(&self, spec: &TableSpec<'_>) -> Result<Statement, StatementError> {
self.0.stamp(spec)
}
fn claim_writes_lease(&self) -> bool {
self.0.claim_writes_lease()
}
fn claim_counts_attempt(&self, spec: &TableSpec<'_>) -> bool {
self.0.claim_counts_attempt(spec)
}
fn begin_lease_claim(&self) -> Option<&'static str> {
self.0.begin_lease_claim()
}
}
impl<DB: BuiltInDialect> Advisory for BuiltIn<DB> {
fn advisory_claim(&self, spec: &TableSpec<'_>) -> Result<Statement, StatementError> {
self.0.advisory_claim(spec)
}
fn lock(&self) -> Option<Statement> {
self.0.lock()
}
fn unlock(&self) -> Option<Statement> {
self.0.unlock()
}
fn take(
&self,
spec: &TableSpec<'_>,
shape: ClaimShape,
) -> Result<Vec<Statement>, StatementError> {
self.0.take(spec, shape)
}
}
#[cfg(feature = "postgres")]
impl RowLock for BuiltIn<Postgres> {
fn lock_claim(
&self,
spec: &TableSpec<'_>,
shape: ClaimShape,
) -> Result<Statement, StatementError> {
self.0.lock_claim(spec, shape)
}
}
#[cfg(feature = "mysql")]
impl RowLock for BuiltIn<MySql> {
fn lock_claim(
&self,
spec: &TableSpec<'_>,
shape: ClaimShape,
) -> Result<Statement, StatementError> {
self.0.lock_claim(spec, shape)
}
}
#[cfg(feature = "any")]
impl RowLock for BuiltIn<Any> {
fn lock_claim(
&self,
spec: &TableSpec<'_>,
shape: ClaimShape,
) -> Result<Statement, StatementError> {
self.0.lock_claim(spec, shape)
}
}
#[cfg(feature = "postgres")]
impl Opens<level::ReadCommitted> for BuiltIn<Postgres> {}
#[cfg(feature = "postgres")]
impl Opens<level::RepeatableRead> for BuiltIn<Postgres> {}
#[cfg(feature = "postgres")]
impl Opens<level::Serializable> for BuiltIn<Postgres> {}
#[cfg(feature = "mysql")]
impl Opens<level::ReadUncommitted> for BuiltIn<MySql> {}
#[cfg(feature = "mysql")]
impl Opens<level::ReadCommitted> for BuiltIn<MySql> {}
#[cfg(feature = "mysql")]
impl Opens<level::RepeatableRead> for BuiltIn<MySql> {}
#[cfg(feature = "mysql")]
impl Opens<level::Serializable> for BuiltIn<MySql> {}
#[cfg(feature = "sqlite")]
impl Opens<level::Deferred> for BuiltIn<Sqlite> {}
#[cfg(feature = "sqlite")]
impl Opens<level::Immediate> for BuiltIn<Sqlite> {}
#[cfg(feature = "sqlite")]
impl Opens<level::Exclusive> for BuiltIn<Sqlite> {}
#[cfg(feature = "any")]
impl Opens<level::ReadUncommitted> for BuiltIn<Any> {}
#[cfg(feature = "any")]
impl Opens<level::ReadCommitted> for BuiltIn<Any> {}
#[cfg(feature = "any")]
impl Opens<level::RepeatableRead> for BuiltIn<Any> {}
#[cfg(feature = "any")]
impl Opens<level::Serializable> for BuiltIn<Any> {}
#[cfg(feature = "any")]
impl Opens<level::Deferred> for BuiltIn<Any> {}
#[cfg(feature = "any")]
impl Opens<level::Immediate> for BuiltIn<Any> {}
#[cfg(feature = "any")]
impl Opens<level::Exclusive> for BuiltIn<Any> {}
#[diagnostic::on_unimplemented(
message = "`{Self}` has no dialect built into the crate",
label = "no built-in dialect for this database",
note = "build the broker with a dialect of the service's own: \
`SqlxBroker::with_dialect(pool, dialect)`, a `SqlxBroker<{Self}, YourDialect>`"
)]
pub trait BuiltInDialect: QueueDatabase {
#[doc(hidden)]
type Picked: Lease + Advisory + Copy + 'static;
fn dialect(conn: &Self::Connection) -> Option<BuiltIn<Self>>;
#[doc(hidden)]
fn backend(conn: &Self::Connection) -> &str {
let _ = conn;
Self::NAME
}
}
#[cfg(feature = "postgres")]
impl BuiltInDialect for Postgres {
type Picked = dialect::Postgres;
fn dialect(_: &PgConnection) -> Option<BuiltIn<Self>> {
Some(BuiltIn::new(dialect::Postgres))
}
}
#[cfg(feature = "mysql")]
impl BuiltInDialect for MySql {
type Picked = dialect::MySql;
fn dialect(_: &MySqlConnection) -> Option<BuiltIn<Self>> {
Some(BuiltIn::new(dialect::MySql))
}
}
#[cfg(feature = "sqlite")]
impl BuiltInDialect for Sqlite {
type Picked = dialect::Sqlite;
fn dialect(_: &SqliteConnection) -> Option<BuiltIn<Self>> {
Some(BuiltIn::new(dialect::Sqlite))
}
}
#[cfg(feature = "any")]
impl BuiltInDialect for Any {
type Picked = AnyDialect;
fn dialect(conn: &AnyConnection) -> Option<BuiltIn<Self>> {
AnyDialect::of(conn.backend_name()).map(BuiltIn::new)
}
fn backend(conn: &AnyConnection) -> &str {
conn.backend_name()
}
}
#[cfg(feature = "any")]
pub(crate) const POSTGRES_BACKEND: &str = "PostgreSQL";
#[cfg(feature = "any")]
pub(crate) const MYSQL_BACKEND: &str = "MySQL";
#[cfg(feature = "any")]
pub(crate) const SQLITE_BACKEND: &str = "SQLite";
#[cfg(all(test, feature = "any"))]
mod tests {
use sqlx::Database;
use super::{MYSQL_BACKEND, POSTGRES_BACKEND, SQLITE_BACKEND};
#[test]
fn the_backend_names_are_those_sqlx_reports() {
assert_eq!(POSTGRES_BACKEND, <sqlx::Postgres as Database>::NAME);
assert_eq!(MYSQL_BACKEND, <sqlx::MySql as Database>::NAME);
assert_eq!(SQLITE_BACKEND, <sqlx::Sqlite as Database>::NAME);
}
}