[][src]Derive Macro postgres_query::FromSqlRow

#[derive(FromSqlRow)]
{
    // Attributes available to this derive:
    #[row]
}

Extract values from a row.

  • If used on a tuple struct, values will be extracted from the corresponding columns based on their position in the tuple.
  • If used on a stuct with named fields, values will be extracted from the column with the same name as the field.

Example

#[derive(FromSqlRow)]
struct TupleData(i32, String);

#[derive(FromSqlRow)]
struct NamedData {
    age: i32,
    name: String,
};

Attributes

Data extraction can be customized by using the #[row(...)] attribute. Attributes can be separated into two categories, those which go on the container itself:

and those which are placed on the container's fields:

Container attributes

These attributes are put on the struct itself.

#[row(exact)]

Partition the row according to the number of columns matched by each group.

Note that no order is forced upon fields within any group. In the example below, that means that even though the generation and origin fields are flipped relative to the query, the extraction will be successful:

#[derive(FromSqlRow)]
#[row(exact)]
struct Family {
    generation: i32,
    origin: String,
    #[row(flatten)]
    parent: Person,
    #[row(flatten)]
    child: Person,
}

#[derive(FromSqlRow)]
struct Person {
    id: i32,
    name: String,
}

let family = query!(
    "SELECT
        'Germany' as origin, 7 as generation,
        1 as id, 'Bob' as name,
        2 as id, 'Ike' as name"
    )
    .fetch_one::<Family, _>(&client)
    .await?;

#[row(split)]

Partition the row according to the field's split points.

Split points are introduced by using the #[row(split = "...")] attribute on fields.

#[derive(FromSqlRow)]
#[row(split)]
struct Family {
    generation: i32,
    origin: String,
    #[row(flatten, split = "id")]
    parent: Person,
    #[row(flatten, split = "id")]
    child: Person,
}

#[derive(FromSqlRow)]
struct Person {
    id: i32,
    name: String,
}

let family = query!(
    "SELECT
        'Germany' as origin, 7 as generation,
        1 as id, 'Bob' as name,
        2 as id, 'Ike' as name"
    )
    .fetch_one::<Family, _>(&client)
    .await?;

#[row(group)]

Enables one-to-many mapping for the container. One-to-many mapping requires that at least one field has the #[row(key)] attribute and that one other field has the #[row(merge)] attribute.

When extracting values from multiple rows, any two adjacent rows that are identical on their fields marked with #[row(key)] will have their fields tagged with #[row(merge)] merged. This means that in order to get the expected relation back, you may need to include a GROUP BY statement in your SQL query, hence the name group.

#[derive(Debug, FromSqlRow)]
#[row(group)]
struct Author {
    #[row(key)]
    name: String,

    #[row(merge)]
    books: Vec<Book>,
}

#[derive(Debug, FromSqlRow)]
struct Book {
    title: String,
}

let authors = query!(
        "SELECT 'J.R.R. Tolkien' as name, 'The Fellowship of the Ring' as title
         UNION ALL SELECT 'J.R.R. Tolkien', 'The Two Towers'
         UNION ALL SELECT 'Andrzej Sapkowski', 'The Last Wish'
         UNION ALL SELECT 'J.R.R. Tolkien', 'Return of the King'")
    .fetch::<Author, _>(&client)
    .await?;

assert_eq!(authors[0].name, "J.R.R. Tolkien");
assert_eq!(authors[0].books[0].title, "The Fellowship of the Ring");
assert_eq!(authors[0].books[1].title, "The Two Towers");

assert_eq!(authors[1].name, "Andrzej Sapkowski");
assert_eq!(authors[1].books[0].title, "The Last Wish");

assert_eq!(authors[2].name, "J.R.R. Tolkien");
assert_eq!(authors[2].books[0].title, "Return of the King");

#[row(hash)]

Like #[row(group)], but all previous rows are considered when merging. This is accomplished by using a HashMap, hence the name. This implies that all keys have to implement the Hash and Eq traits:

#[derive(Debug, FromSqlRow)]
#[row(hash)]
struct Author {
    #[row(key)]
    name: String,

    #[row(merge)]
    books: Vec<Book>,
}

#[derive(Debug, FromSqlRow)]
struct Book {
    title: String,
}

let authors = query!(
        "SELECT 'J.R.R. Tolkien' as name, 'The Fellowship of the Ring' as title
         UNION ALL SELECT 'J.R.R. Tolkien', 'The Two Towers'
         UNION ALL SELECT 'Andrzej Sapkowski', 'The Last Wish'
         UNION ALL SELECT 'J.R.R. Tolkien', 'Return of the King'")
    .fetch::<Author, _>(&client)
    .await?;

assert_eq!(authors[0].name, "J.R.R. Tolkien");
assert_eq!(authors[0].books[0].title, "The Fellowship of the Ring");
assert_eq!(authors[0].books[1].title, "The Two Towers");
assert_eq!(authors[0].books[2].title, "Return of the King");

assert_eq!(authors[1].name, "Andrzej Sapkowski");
assert_eq!(authors[1].books[0].title, "The Last Wish");

Field attributes

These attributes are put on the fields of a container.

#[row(rename = "...")]

Use a name other than that of the field when looking up the name of the column.

#[derive(FromSqlRow)]
struct Person {
    age: i32,
    // matches the column named "first_name" instead of "name"
    #[row(rename = "first_name")]
    name: String,
}

#[row(flatten)]

Flatten the contents of this field into its container by recursively calling FromSqlRow on the field's type. This removes one level of nesting:

#[derive(FromSqlRow)]
struct Customer {
    id: i32,
    #[row(flatten)]
    info: Person,
}

#[derive(FromSqlRow)]
struct Person {
    name: String,
    age: i32
}

let customer: Customer = query!("SELECT 14 as id, 'Bob' as name, 47 as age")
    .fetch_one(&client)
    .await?;

assert_eq!(customer.id, 14);
assert_eq!(customer.info.name, "Bob");
assert_eq!(customer.info.age, 47);

#[row(stride = N)]

Puts this field into a partition with exactly N columns. Only available when using the #[row(exact)] attribute on the container,

#[derive(Debug, FromSqlRow)]
struct Person {
    id: i32,
    name: String,
}

#[derive(Debug, FromSqlRow)]
#[row(exact)]
struct Family {
    // Matches first 4 columns
    #[row(flatten, stride = 4)]
    parent: Person,
    // Matches last 3 columns
    #[row(flatten, stride = 3)]
    child: Person,
}

let family = query!(
    "SELECT
        11 as generation,
        1 as id, 'Bob' as name, 42 as age,
        2 as id, 'Ike' as name, 14 as age"
    )
    .fetch_one::<Family, _>(&client)
    .await?;
     
assert_eq!(family.parent.id, 1);
assert_eq!(family.parent.name, "Bob");
assert_eq!(family.child.id, 2);
assert_eq!(family.child.name, "Ike");

#[row(split = "...")]

Introduce an additional split right before this field. Requires that the container has the split attribute as well.

Intuitively this splits the row in two parts: every field before this attribute matches the columns before the split and every field afterwards matches the second remaining columns.

#[derive(FromSqlRow)]
#[row(split)]
struct User {
    // `id` and `name` will only match the columns before `email`
    id: i32,
    name: String,
    #[row(split = "email")]
    // `email`, `address` and `shoe_size` will only
    // match the columns after and including `email`
    email: String,
    address: String,
    shoe_size: i32,
}

Note that the first split always matches first occurence of that column. This can result in some subtle bugs:

#[derive(FromSqlRow)]
#[row(split)]
struct Family {
    #[row(flatten)]
    parent: Person,
    #[row(flatten, split = "id")]
    child: Person,
}

#[derive(FromSqlRow)]
struct Person {
    name: String,
    age: i32
}

let query = query!("SELECT parent.*, child.* FROM ...");

// Imagine the query above results in the following columns:
//
// Columns:                id, name, id, name
// Splits:                |
// Partitions:  +-parent-+ +-----child------+

The split causes parent to match against all columns before the first id, ie. an empty partition. This would cause an error when executing the query.

A correct split would look like this:

#[derive(FromSqlRow)]
#[row(split)]
struct Family {
    #[row(flatten, split = "id")]
    parent: Person,
    #[row(flatten, split = "id")]
    child: Person,
}

#[row(key)]

Specifies this field to be a key field. key fields are compared against each other when extracting values from multiple rows. Rows are merged if the key fields in each row are identical. You may have multiple key fields within a single container, but none of them may have the #[row(merge)] attribute. Multiple key fields will be treated as a tuple in comparisons.

#[row(merge)]

Specifies this field to be a merge field. This requires that the field's type implements the Merge trait. When two rows have been deemed to be equal based on the key fields, the corresponding merge fields in those rows will be merged. You may specify multiple merge fields within one container, but none of them may have the #[row(key)] attribute.