ruststream_sqlx/inbox/database/
built_in.rs1use std::fmt;
5use std::num::NonZeroUsize;
6
7#[cfg(any(feature = "postgres", feature = "mysql", feature = "sqlite"))]
8use ruststream_sqlx_dialect as dialect;
9#[cfg(any(feature = "postgres", feature = "mysql", feature = "any"))]
10use ruststream_sqlx_dialect::RowLock;
11use ruststream_sqlx_dialect::{
12 Advisory, ClaimShape, Dialect, Lease, Opening, Statement, StatementError, TableName, TableSpec,
13};
14#[cfg(any(
15 feature = "postgres",
16 feature = "mysql",
17 feature = "sqlite",
18 feature = "any"
19))]
20use ruststream_sqlx_dialect::{Opens, level};
21#[cfg(feature = "any")]
22use sqlx::{Any, AnyConnection};
23#[cfg(feature = "mysql")]
24use sqlx::{MySql, MySqlConnection};
25#[cfg(feature = "postgres")]
26use sqlx::{PgConnection, Postgres};
27#[cfg(feature = "sqlite")]
28use sqlx::{Sqlite, SqliteConnection};
29
30use super::QueueDatabase;
31#[cfg(feature = "any")]
32use super::any::AnyDialect;
33
34pub struct BuiltIn<DB: BuiltInDialect>(DB::Picked);
105
106impl<DB: BuiltInDialect> BuiltIn<DB> {
107 #[cfg(any(
109 feature = "postgres",
110 feature = "mysql",
111 feature = "sqlite",
112 feature = "any"
113 ))]
114 pub(crate) const fn new(picked: DB::Picked) -> Self {
115 Self(picked)
116 }
117}
118
119impl<DB: BuiltInDialect> Clone for BuiltIn<DB> {
120 fn clone(&self) -> Self {
121 *self
122 }
123}
124
125impl<DB: BuiltInDialect> Copy for BuiltIn<DB> {}
126
127impl<DB: BuiltInDialect> fmt::Debug for BuiltIn<DB> {
128 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
129 f.debug_tuple("BuiltIn").field(&self.0.name()).finish()
130 }
131}
132
133impl<DB: BuiltInDialect> Dialect for BuiltIn<DB> {
134 fn name(&self) -> &'static str {
135 self.0.name()
136 }
137
138 fn quote_into(&self, ident: &str, out: &mut String) {
139 self.0.quote_into(ident, out);
140 }
141
142 fn placeholder_into(&self, index: NonZeroUsize, out: &mut String) {
143 self.0.placeholder_into(index, out);
144 }
145
146 fn fetch(&self, spec: &TableSpec<'_>) -> Result<Statement, StatementError> {
147 self.0.fetch(spec)
148 }
149
150 fn ack(&self, spec: &TableSpec<'_>) -> Result<Statement, StatementError> {
151 self.0.ack(spec)
152 }
153
154 fn retry(&self, spec: &TableSpec<'_>) -> Result<Option<Statement>, StatementError> {
155 self.0.retry(spec)
156 }
157
158 fn retry_after(&self, spec: &TableSpec<'_>) -> Result<Statement, StatementError> {
159 self.0.retry_after(spec)
160 }
161
162 fn discard(&self, spec: &TableSpec<'_>) -> Result<Statement, StatementError> {
163 self.0.discard(spec)
164 }
165
166 fn dead_letter_group(&self, spec: &TableSpec<'_>) -> Result<Statement, StatementError> {
167 self.0.dead_letter_group(spec)
168 }
169
170 fn dead_letter_table(
171 &self,
172 spec: &TableSpec<'_>,
173 target: TableName<'_>,
174 ) -> Result<Vec<Statement>, StatementError> {
175 self.0.dead_letter_table(spec, target)
176 }
177
178 fn insert(&self, spec: &TableSpec<'_>) -> Result<Statement, StatementError> {
179 self.0.insert(spec)
180 }
181
182 fn server_version(&self) -> Option<&'static str> {
183 self.0.server_version()
184 }
185
186 fn check_server(&self, spec: &TableSpec<'_>, version: &str) -> Result<(), StatementError> {
187 self.0.check_server(spec, version)
188 }
189
190 fn begin(&self, opening: Opening) -> Result<Option<&'static str>, StatementError> {
191 self.0.begin(opening)
192 }
193
194 fn savepoint(&self) -> &'static str {
195 self.0.savepoint()
196 }
197
198 fn rollback_to_savepoint(&self) -> &'static str {
199 self.0.rollback_to_savepoint()
200 }
201
202 fn fifo_guard(&self, spec: &TableSpec<'_>) -> Result<Option<Statement>, StatementError> {
203 self.0.fifo_guard(spec)
204 }
205}
206
207impl<DB: BuiltInDialect> Lease for BuiltIn<DB> {
208 fn lease_claim(
209 &self,
210 spec: &TableSpec<'_>,
211 shape: ClaimShape,
212 ) -> Result<Statement, StatementError> {
213 self.0.lease_claim(spec, shape)
214 }
215
216 fn extend(&self, spec: &TableSpec<'_>) -> Result<Statement, StatementError> {
217 self.0.extend(spec)
218 }
219
220 fn stamp(&self, spec: &TableSpec<'_>) -> Result<Statement, StatementError> {
221 self.0.stamp(spec)
222 }
223
224 fn claim_writes_lease(&self) -> bool {
225 self.0.claim_writes_lease()
226 }
227
228 fn claim_counts_attempt(&self, spec: &TableSpec<'_>) -> bool {
229 self.0.claim_counts_attempt(spec)
230 }
231
232 fn begin_lease_claim(&self) -> Option<&'static str> {
233 self.0.begin_lease_claim()
234 }
235}
236
237impl<DB: BuiltInDialect> Advisory for BuiltIn<DB> {
238 fn advisory_claim(&self, spec: &TableSpec<'_>) -> Result<Statement, StatementError> {
239 self.0.advisory_claim(spec)
240 }
241
242 fn lock(&self) -> Option<Statement> {
243 self.0.lock()
244 }
245
246 fn unlock(&self) -> Option<Statement> {
247 self.0.unlock()
248 }
249
250 fn take(
251 &self,
252 spec: &TableSpec<'_>,
253 shape: ClaimShape,
254 ) -> Result<Vec<Statement>, StatementError> {
255 self.0.take(spec, shape)
256 }
257}
258
259#[cfg(feature = "postgres")]
260impl RowLock for BuiltIn<Postgres> {
261 fn lock_claim(
262 &self,
263 spec: &TableSpec<'_>,
264 shape: ClaimShape,
265 ) -> Result<Statement, StatementError> {
266 self.0.lock_claim(spec, shape)
267 }
268}
269
270#[cfg(feature = "mysql")]
271impl RowLock for BuiltIn<MySql> {
272 fn lock_claim(
273 &self,
274 spec: &TableSpec<'_>,
275 shape: ClaimShape,
276 ) -> Result<Statement, StatementError> {
277 self.0.lock_claim(spec, shape)
278 }
279}
280
281#[cfg(feature = "any")]
282impl RowLock for BuiltIn<Any> {
283 fn lock_claim(
284 &self,
285 spec: &TableSpec<'_>,
286 shape: ClaimShape,
287 ) -> Result<Statement, StatementError> {
288 self.0.lock_claim(spec, shape)
289 }
290}
291
292#[cfg(feature = "postgres")]
293impl Opens<level::ReadCommitted> for BuiltIn<Postgres> {}
294
295#[cfg(feature = "postgres")]
296impl Opens<level::RepeatableRead> for BuiltIn<Postgres> {}
297
298#[cfg(feature = "postgres")]
299impl Opens<level::Serializable> for BuiltIn<Postgres> {}
300
301#[cfg(feature = "mysql")]
302impl Opens<level::ReadUncommitted> for BuiltIn<MySql> {}
303
304#[cfg(feature = "mysql")]
305impl Opens<level::ReadCommitted> for BuiltIn<MySql> {}
306
307#[cfg(feature = "mysql")]
308impl Opens<level::RepeatableRead> for BuiltIn<MySql> {}
309
310#[cfg(feature = "mysql")]
311impl Opens<level::Serializable> for BuiltIn<MySql> {}
312
313#[cfg(feature = "sqlite")]
314impl Opens<level::Deferred> for BuiltIn<Sqlite> {}
315
316#[cfg(feature = "sqlite")]
317impl Opens<level::Immediate> for BuiltIn<Sqlite> {}
318
319#[cfg(feature = "sqlite")]
320impl Opens<level::Exclusive> for BuiltIn<Sqlite> {}
321
322#[cfg(feature = "any")]
326impl Opens<level::ReadUncommitted> for BuiltIn<Any> {}
327
328#[cfg(feature = "any")]
329impl Opens<level::ReadCommitted> for BuiltIn<Any> {}
330
331#[cfg(feature = "any")]
332impl Opens<level::RepeatableRead> for BuiltIn<Any> {}
333
334#[cfg(feature = "any")]
335impl Opens<level::Serializable> for BuiltIn<Any> {}
336
337#[cfg(feature = "any")]
338impl Opens<level::Deferred> for BuiltIn<Any> {}
339
340#[cfg(feature = "any")]
341impl Opens<level::Immediate> for BuiltIn<Any> {}
342
343#[cfg(feature = "any")]
344impl Opens<level::Exclusive> for BuiltIn<Any> {}
345
346#[diagnostic::on_unimplemented(
401 message = "`{Self}` has no dialect built into the crate",
402 label = "no built-in dialect for this database",
403 note = "build the broker with a dialect of the service's own: \
404 `SqlxBroker::with_dialect(pool, dialect)`, a `SqlxBroker<{Self}, YourDialect>`"
405)]
406pub trait BuiltInDialect: QueueDatabase {
407 #[doc(hidden)]
410 type Picked: Lease + Advisory + Copy + 'static;
411
412 fn dialect(conn: &Self::Connection) -> Option<BuiltIn<Self>>;
418
419 #[doc(hidden)]
422 fn backend(conn: &Self::Connection) -> &str {
423 let _ = conn;
424 Self::NAME
425 }
426}
427
428#[cfg(feature = "postgres")]
429impl BuiltInDialect for Postgres {
430 type Picked = dialect::Postgres;
431
432 fn dialect(_: &PgConnection) -> Option<BuiltIn<Self>> {
433 Some(BuiltIn::new(dialect::Postgres))
434 }
435}
436
437#[cfg(feature = "mysql")]
438impl BuiltInDialect for MySql {
439 type Picked = dialect::MySql;
440
441 fn dialect(_: &MySqlConnection) -> Option<BuiltIn<Self>> {
442 Some(BuiltIn::new(dialect::MySql))
443 }
444}
445
446#[cfg(feature = "sqlite")]
447impl BuiltInDialect for Sqlite {
448 type Picked = dialect::Sqlite;
449
450 fn dialect(_: &SqliteConnection) -> Option<BuiltIn<Self>> {
451 Some(BuiltIn::new(dialect::Sqlite))
452 }
453}
454
455#[cfg(feature = "any")]
456impl BuiltInDialect for Any {
457 type Picked = AnyDialect;
458
459 fn dialect(conn: &AnyConnection) -> Option<BuiltIn<Self>> {
460 AnyDialect::of(conn.backend_name()).map(BuiltIn::new)
461 }
462
463 fn backend(conn: &AnyConnection) -> &str {
464 conn.backend_name()
465 }
466}
467
468#[cfg(feature = "any")]
471pub(crate) const POSTGRES_BACKEND: &str = "PostgreSQL";
472
473#[cfg(feature = "any")]
475pub(crate) const MYSQL_BACKEND: &str = "MySQL";
476
477#[cfg(feature = "any")]
479pub(crate) const SQLITE_BACKEND: &str = "SQLite";
480
481#[cfg(all(test, feature = "any"))]
482mod tests {
483 use sqlx::Database;
484
485 use super::{MYSQL_BACKEND, POSTGRES_BACKEND, SQLITE_BACKEND};
486
487 #[test]
488 fn the_backend_names_are_those_sqlx_reports() {
489 assert_eq!(POSTGRES_BACKEND, <sqlx::Postgres as Database>::NAME);
490 assert_eq!(MYSQL_BACKEND, <sqlx::MySql as Database>::NAME);
491 assert_eq!(SQLITE_BACKEND, <sqlx::Sqlite as Database>::NAME);
492 }
493}