launchbadge / sqlx

🧰 The Rust SQL Toolkit. An async, pure Rust SQL crate featuring compile-time checked queries without a DSL. Supports PostgreSQL, MySQL, and SQLite.
Apache License 2.0
12.92k stars 1.23k forks source link

Implementation of `Executor` is not general enough #3150

Open KunoiSayami opened 5 months ago

KunoiSayami commented 5 months ago

Bug Description

When try use execute() function in raw_sql , it cause this. But execute_many() is OK.

This error only shown in tokio::spawn(), if remove tokio::spawn(), error disappear.

error: implementation of `Executor` is not general enough
  --> src/main.rs:9:13
   |
9  | /             tokio::spawn(async {
10 | |                 let mut db = SqliteConnection::connect(":memory:").await.unwrap();
11 | |                 sqlx::raw_sql("").execute(&mut db).await.unwrap();
12 | |                 db.close().await.unwrap();
13 | |             })
   | |______________^ implementation of `Executor` is not general enough
   |
   = note: `Executor<'_>` would have to be implemented for the type `&mut SqliteConnection`
   = note: ...but `Executor<'0>` is actually implemented for the type `&'0 mut SqliteConnection`, for some specific lifetime `'0`

Minimal Reproduction

use sqlx::{Connection, SqliteConnection};

fn main() {
    tokio::runtime::Builder::new_multi_thread()
        .enable_all()
        .build()
        .unwrap()
        .block_on(async {
            tokio::spawn(async {
                let mut db = SqliteConnection::connect(":memory:").await.unwrap();
                sqlx::raw_sql("").execute(&mut db).await.unwrap();
                db.close().await.unwrap();
            })
            .await
        });
}

Info

bionicles commented 5 months ago

I must admit I'm also struggling with Executor for several hours today. For some reason, attempts to make a function generic over DB: Database lead to repeated suggestions to add deeply nested trait bounds to tell rustc things which are definitely true are indeed true, and even adding the bounds didn't work to satisfy this "execute" function.

error[E0277]: the trait bound for<'c> &'c mut <DB as sqlx::Database>::Connection: sqlx::Executor<'c> is not satisfied --> src/lib.rs:244:47 244 match sqlx::query(&sql_statement).execute(command.pool).await { ------- ^^^^^^^^^^^^ the trait for<'c> sqlx::Executor<'c> is not implemented for &'c mut <DB as sqlx::Database>::Connection, which is required by &sqlx::Pool<DB>: sqlx::Executor<'_>
required by a bound introduced by this call
= note: required for `&sqlx::Pool<DB>` to implement `sqlx::Executor<'_>`
note: required by a bound in sqlx::query::Query::<'q, DB, A>::execute --> /home/bion/.cargo/registry/src/index.crates.io-6f17d22bba15001f/sqlx-core-0.7.4/src/query.rs:155:12 151 pub async fn execute<'e, 'c: 'e, E>(self, executor: E) -> Result<DB::QueryResult, Error> ------- required by a bound in this associated function ... 155 E: Executor<'c, Database = DB>, ^^^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in Query::<'q, DB, A>::execute help: consider introducing a where clause, but there might be an alternative better way to express this requirement
192 ) -> Result<InsertReceipt, DFUtilError> where for<'c> &'c mut ::Connection: sqlx::Executor<'c> {
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Likewise with arguments,

error[E0599]: the method fetch_all exists for struct Query<'_, DB, <DB as HasArguments<'_>>::Arguments>, but its trait bounds were not satisfied --> src/lib.rs:134:40 134 let query_result = match sql_query.fetch_all(pool_ref_for_db).await { ^^^^^^^^^ method cannot be called on Query<'_, DB, <DB as HasArguments<'_>>::Arguments> due to unsatisfied trait bounds
= note: the following trait bounds were not satisfied:
        `<DB as sqlx::database::HasArguments<'_>>::Arguments: sqlx::IntoArguments<'_, DB>`
and with Types, make sure they're both Type and Encode! : error[E0277]: the trait bound i16: sqlx::Encode<'_, DB> is not satisfied --> src/lib.rs:103:51 103 AnyValue::UInt8(v) => b.push_bind(v as i16), --------- ^^^^^^^^ the trait sqlx::Encode<'_, DB> is not implemented for i16
required by a bound introduced by this call
note: required by a bound in sqlx::query_builder::Separated::<'qb, 'args, DB, Sep>::push_bind --> /home/bion/.cargo/registry/src/index.crates.io-6f17d22bba15001f/sqlx-core-0.7.4/src/query_builder.rs:572:20 570 pub fn push_bind(&mut self, value: T) -> &mut Self --------- required by a bound in this associated function 571 where 572 T: 'args + Encode<'args, DB> + Send + Type, ^^^^^^^^^^^^^^^^^ required by this bound in Separated::<'qb, 'args, DB, Sep>::push_bind help: consider extending the where clause, but there might be an alternative better way to express this requirement
29 &'a Pool: sqlx::Executor<'a, Database = DB>, i16: sqlx::Encode<'_, DB>
~~~~~~~
error[E0277]: the trait bound bool: sqlx::Type<DB> is not satisfied --> src/lib.rs:102:53 102 AnyValue::Boolean(v) => b.push_bind(v), --------- ^ the trait sqlx::Type<DB> is not implemented for bool
required by a bound introduced by this call
note: required by a bound in sqlx::query_builder::Separated::<'qb, 'args, DB, Sep>::push_bind --> /home/bion/.cargo/registry/src/index.crates.io-6f17d22bba15001f/sqlx-core-0.7.4/src/query_builder.rs:572:47 570 pub fn push_bind(&mut self, value: T) -> &mut Self --------- required by a bound in this associated function 571 where 572 T: 'args + Encode<'args, DB> + Send + Type, ^^^^^^^^ required by this bound in Separated::<'qb, 'args, DB, Sep>::push_bind help: consider extending the where clause, but there might be an alternative better way to express this requirement
29 &'a Pool: sqlx::Executor<'a, Database = DB>, bool: sqlx::Type
~~~~~~

I really like the concept of sqlx but every time I try to use it (for a library), it feels hard. How can we make it easier?

async fn bulk_existence_check<'a, DB: Database>(
    query: BulkExistenceCheck<'a, DB>,
) -> Result<BulkExistenceReceipt<'a>, DFUtilError>
where
    &'a Pool<DB>: sqlx::Executor<'a, Database = DB>

I guess I figured Database would be "enough" for Pool to execute commands and for number types to know how to go into sql. Seems like I need to add a bound for every Type of thing and specify that yes, you can Encode them for this Database, and make sure the Pool and the PoolConnection Connection are all Executor, and don't forget that the Arguments in HasArguments need to implement IntoArguments ?

Ugh. How do we make this simpler, please? Is Database just a marker and I'm using the wrong generic here? I'd love to just hardcode everything for Postgres but I can't because I need to support MySQL. And SQLite is also a Database so I had hoped to abstract over all of them. Is that a pipe dream?

stepantubanov commented 2 months ago

Until this is fixed as a workaround you can do this:

// `conn` implements `sqlx::Executor`
conn.execute(sqlx::raw_sql("...")).await.unwrap();
yuyang-ok commented 1 month ago

I have the same issue .