kurtbuilds / ormlite

An ORM in Rust for developers that love SQL.
https://crates.io/crates/ormlite
MIT License
216 stars 11 forks source link

Provide get_many #5

Closed heroin-moose closed 2 years ago

heroin-moose commented 2 years ago

Currently it's possible to get one single entry using Example::get_one(). However, it's not clear how to get many entries without manually writing the query. So Example::get_many(&mut conn) would be nice to have.

kurtbuilds commented 2 years ago

In your hypothetical get_many API, how would the user do filtering?

The second to last example in the usage section is this example:

    /// You can create a query builder.
    let people = Person::select()
            .filter("age > ?").bind(50)
            .fetch_all(&mut conn).await?;

And it can of course be used without the filter and bind calls.

Does that suit your needs?

heroin-moose commented 2 years ago

It gives my type errors without filter or bind, so I was under the impression that select() is a builder of a sort.

heroin-moose commented 2 years ago

EDIT: Spoke too soon, both variants do not work.

Some weird is happening. I'm using ormlite in conjunction with poem and the following works:

async fn foobar(...) -> Result<Vec<Type>> {
    let v = Type::select().fetch_all(pool).await?;
    Ok(v)
}

However this does not:

async fn foobar(...) -> Result<Vec<Type>> {
    Type::select().fetch_all(pool).await
}

The type error suggests that Type::select() may be used later than await for some reason.

heroin-moose commented 2 years ago

Code:

use ormlite::Model;
use ormlite::model::*;
use poem::EndpointExt;
use poem::Route;
use poem::Server;
use poem::error::InternalServerError;
use poem::handler;
use poem::listener::UnixListener;
use poem::middleware::AddData;
use poem::web::Data;
use sqlx::FromRow;
use poem::get;
use sqlx::SqlitePool;

#[derive(Model, FromRow)]
struct Example {
    #[ormlite(primary_key)]
    name: String
}

#[tokio::main]
async fn main() -> anyhow::Result<()> {
    let pool = SqlitePool::connect("/tmp/database").await?;
    let socket = UnixListener::bind("/tmp/pxed.sock");

    Server::new(socket).run(
        Route::new()
            .nest("/", get(endpoint))
            .with(AddData::new(pool))
    ).await?;

    Ok(())
}

#[handler]
async fn endpoint(pool: Data<&SqlitePool>) -> poem::Result<()> {
    query(*pool).await.map_err(InternalServerError)
}

async fn query(pool: &SqlitePool) -> ormlite::Result<()> {
    Example::select().fetch_all(pool).await.map(|_| ())
}

Error:

error: future cannot be sent between threads safely
  --> src/main.rs:35:1
   |
35 | #[handler]
   | ^^^^^^^^^^ future created by async block is not `Send`
   |
   = help: the trait `Send` is not implemented for `dyn Iterator<Item = String>`
note: future is not `Send` as this value is used across an await
  --> src/main.rs:41:5
   |
41 |     Example::select().fetch_all(pool).await.map(|_| ())
   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ first, await occurs here, with `Example::select()` maybe used later...
note: `Example::select()` is later dropped here
  --> src/main.rs:42:1
   |
41 |     Example::select().fetch_all(pool).await.map(|_| ())
   |     ----------------- has type `SelectQueryBuilder<'_, sqlx::Sqlite, Example, Box<dyn Iterator<Item = String>>>` which is not `Send`
42 | }
   | ^
   = note: required for the cast to the object type `dyn Future<Output = Result<poem::Response, poem::Error>> + Send`
   = note: this error originates in the attribute macro `handler` (in Nightly builds, run with -Z macro-backtrace for more info)

error: could not compile `ormlite-issue` due to previous error

I'm not sure what happens, but looks suspicious.

kurtbuilds commented 2 years ago

Thank you for providing such a clearcut repro example. It made it really easy to isolate this issue.

It seems we were missing a Send bound on SelectQueryBuilder. I fixed this in a9292c8, then cut 0.1.4.

I created a repo (https://github.com/kurtbuilds/ormlite-issue) based on your code, and confirmed that it is building correctly now.

Can you confirm this fixes your issue, and if so, close this ticket?


Partially as a note to myself, it might make sense to have a dedicated struct for the PlaceholderGenerator instead of having it as a Box<dyn ...>, which would simplify some of the trait bounds.

heroin-moose commented 2 years ago

Yep, works for me.