sfackler / rust-postgres

Native PostgreSQL driver for the Rust programming language
Apache License 2.0
3.49k stars 443 forks source link

FromSql a whole row into a struct #978

Closed tv42 closed 1 year ago

tv42 commented 1 year ago

Hi. I'd like to fetch rows directly into a struct, without going field by field and doing let field1: MyType = row.get(0) etc. Something like let person: Person = row.get_struct(), which can reuse the composite type logic to parse into a struct deriving FromSql.

You can currently hack around this with the following trick. Here, I'm using the fact that Postgres already defines a composite type for every table; for this trick to work with other than SELECT *, you'd need to define a PG composite type for each query too.

#[derive(Debug, ToSql, FromSql)]
#[postgres(name = "person")]
struct Person {
    id: i64,
    name: String,
    data: Option<Vec<u8>>,
}

fn main() -> Result<(), Box<dyn std::error::Error>> {
    let mut client = postgres::Client::connect(
        "host=localhost user=postgres",
        postgres::NoTls,
    )?;

    client.batch_execute(
        "
        CREATE TABLE IF NOT EXISTS person (
            id      BIGSERIAL PRIMARY KEY,
            name    TEXT NOT NULL,
            data    BYTEA
        )
    ",
    )?;

    {
        let name = "Ferris";
        client.execute(
            "INSERT INTO person (name) VALUES ($1)",
            &[&name],
        )?;
    }

    for row in client.query(
        // Jump through hoops to make postgres-types work well.
        "SELECT row(person.*)::person FROM person",
        &[],
    )? {
        let person: Person = row.get(0);
        println!("found person: {:?}", person);
    }

It'd be nice to avoid that extra row(...)::person trickery, and to not need to define Postgres-side composite types for every possible combination of selected columns. postgres-types should be able to enumerate the columns and match each one to a struct field, just like it does with composite types (without needing a composite type).

sfackler commented 1 year ago

As you noted, the composite type can be used in some cases.

Building logic to deserialize an entire row into a Rust struct is something that I think would be best developed in a separate project.