Im trying to extend sqlx Query
generically to provide a "finisher" (like execute()
, fetch_one()
, etc) that will also write a message to an outbox table. The method should take a Transaction
, and should reject any other thing that derefs to Executor
, since I wan't to be absolutley sure that the original query and the INSERT
to my outbox table happens within the same transaction - but I'm running into issues setting up the trait bounds for it.
I've got this:
// ------------------------------------------------------------
// Trait definition
// ------------------------------------------------------------
pub trait Publishable<'q, DB>
where
DB: Database,
{
const OUTBOX_INSERT: &'static str = r#"
INSERT INTO outbox (id, channel, message)
VALUES ($1, $2, $3, $4)
"#;
///
async fn outbox_transact<'a>(
self,
tx: &'a mut Transaction<'a, DB>,
) -> Result<<DB as Database>::QueryResult, sqlx::Error>;
}
// ------------------------------------------------------------
// Geneirc implmentation for Query
// ------------------------------------------------------------
impl<'q, DB, A> Publishable<'q, DB> for Query<'q, DB, A>
where
DB: Database,
A: IntoArguments<'q, DB> + Send + 'q,
for<'c> &'c mut Transaction<'c, DB>: Executor<'c, Database = DB>,
{
async fn outbox_transact<'a>(
self,
tx: &'a mut Transaction<'a, DB>,
) -> Result<<DB as Database>::QueryResult, sqlx::Error> {
self.execute(tx).await
}
}
Now the above seems to check out at first, but when I try to invoke it on a query like this:
let mut tx = pool.begin().await?;
let query = sqlx::query(
r#"
INSERT INTO example (data)
VALUES ($1)
"#,
)
.bind(1);
query.outbox_transact(&mut *tx).await?; // <<< compile error here
tx.commit().await?;
Then I run into this compiler error:
error[E0599]: the method `outbox_transact` exists for struct `Query<'_, _, _>`, but its trait bounds were not satisfied
--> src/outbox/driven.rs:199:15
|
199 | query.outbox_transact(&mut *tx).await?;
| ^^^^^^^^^^^^^^^ method cannot be called on `Query<'_, _, _>` due to unsatisfied trait bounds
|
::: /home/jakob/.cargo/registry/src/index.crates.io-6f17d22bba15001f/sqlx-core-0.8.2/src/query.rs:17:1
|
17 | pub struct Query<'q, DB: Database, A> {
| ------------------------------------- doesn't satisfy `_: Publishable<'_, _>`
|
note: the following trait bounds were not satisfied:
`&'c mut Transaction<'c, _>: sqlx::Executor<'c>`
`<&'c mut Transaction<'c, _> as sqlx::Executor<'c>>::Database = _`
--> src/outbox/driven.rs:154:42
|
150 | impl<'q, DB, A> Publishable<'q, DB> for Query<'q, DB, A>
| ------------------- ----------------
...
154 | for<'c> &'c mut Transaction<'c, DB>: Executor<'c, Database = DB>,
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^
| | |
| | unsatisfied trait bound introduced here
| unsatisfied trait bound introduced here
= help: items from traits can only be used if the trait is implemented and in scope
note: `outbox::driven::Publishable` defines an item `outbox_transact`, perhaps you need to implement it
--> src/outbox/driven.rs:131:1
|
131 | pub trait Publishable<'q, DB>
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Then, in trying to fix it I loosen the bounds on the impl to:
for<'c> &'c mut <DB as Database>::Connection: Executor<'c, Database = DB>
...but then I instead get into errors on the call to execute()
within my trait impl
error[E0277]: the trait bound `&mut Transaction<'a, DB>: sqlx::Executor<'_>` is not satisfied
--> src/outbox/driven.rs:160:26
|
160 | self.execute(tx).await
| ^^^^^ the trait `sqlx::Executor<'_>` is not implemented for `&mut Transaction<'a, DB>`
|
= help: the following other types implement trait `sqlx::Executor<'c>`:
`&'c mut AnyConnection` implements `sqlx::Executor<'c>`
`&'c mut MySqlConnection` implements `sqlx::Executor<'c>`
`&'c mut PgConnection` implements `sqlx::Executor<'c>`
`&'c mut PgListener` implements `sqlx::Executor<'c>`
`&'c mut SqliteConnection` implements `sqlx::Executor<'c>`
`&Pool<DB>` implements `sqlx::Executor<'p>`
note: required by a bound in `sqlx::query::Query::<'q, DB, A>::execute`
--> /home/jakob/.cargo/registry/src/index.crates.io-6f17d22bba15001f/sqlx-core-0.8.2/src/query.rs:188:12
|
184 | pub async fn execute<'e, 'c: 'e, E>(self, executor: E) -> Result<DB::QueryResult, Error>
| ------- required by a bound in this associated function
...
188 | E: Executor<'c, Database = DB>,
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `Query::<'q, DB, A>::execute`
Would be very grateful for any insight on the above, I'm pretty stuck with this.