liveBook · Manning

Query_as! macro

As explained in Rust Servers, Services, and Apps

query_as! is an SQLx compile-time macro that maps SQL query results into a specified Rust type and checks the query against the database schema.

Use `sqlx::query_as!` in a database-access function when SQL results should become a named Rust struct. For course queries, it maps returned columns into `Course` fields without writing an assignment for every field. The `Course` model derives `sqlx::FromRow`. section 6.2.2 section 6.4

use crate::errors::EzyTutorError;
use crate::models::course::*;
use sqlx::postgres::PgPool;
 
 
pub async fn get_courses_for_tutor_db(
    pool: &PgPool,
    tutor_id: i32,
) -> Result<Vec<Course>, EzyTutorError> {
    // Prepare SQL statement
 
    let course_rows: Vec<Course> = sqlx::query_as!(    #1
        Course,
        "SELECT * FROM ezy_course_c6 where tutor_id = $1",
        tutor_id
    )
    .fetch_all(pool)                                   #2
    .await?;                                           #3
 
    Ok(course_rows)                                    #4
}

In `src/iter5/dbaccess/course.rs`, the tutor-course query names `Course`, supplies SQL and `tutor_id`, then uses `fetch_all(pool)` to return `Vec<Course>`. section 6.2.2

Choose the fetch method to match the operation: `fetch_optional` lets a single-course lookup turn a missing row into a not-found error; an INSERT with PostgreSQL `RETURNING` and `fetch_one` returns the newly created `Course`. A course update first fetches the current row so omitted optional fields can keep their existing values. section 6.2.2

use crate::errors::EzyTutorError;
use crate::models::tutor::{NewTutor, Tutor, UpdateTutor};
use sqlx::postgres::PgPool;
 
pub async fn get_all_tutors_db(pool: &PgPool) ->
Result<Vec<Tutor>, EzyTutorError> {
    // Prepare SQL statement
    let tutor_rows =
        sqlx::query!("SELECT tutor_id, tutor_name, tutor_pic_url,
        tutor_profile FROM ezy_tutor_c6")
            .fetch_all(pool)
            .await?;
    // Extract result
 
    let tutors: Vec<Tutor> = tutor_rows
        .iter()
        .map(|tutor_row| Tutor {
            tutor_id: tutor_row.tutor_id,
            tutor_name: tutor_row.tutor_name.clone(),
            tutor_pic_url: tutor_row.tutor_pic_url.clone(),
            tutor_profile: tutor_row.tutor_profile.clone(),
        })
        .collect();
    match tutors.len() {
        0 => Err(EzyTutorError::NotFound("No tutors found".into())),
        _ => Ok(tutors),
    }
}

Compare the tutor query: `sqlx::query!` fetches rows, and a `map` closure assigns each column to a `Tutor` field. Manual mapping is useful when struct fields do not match database columns, such as when a struct adds computed fields or includes a tutor’s courses. section 6.3.3

How Rust Servers, Services, and Apps covers it

How deep it goes
Works through course reads and inserts, then contrasts manual tutor mapping; does not explain macro internals.
Examples
Shows tutor-filtered course reads, a single-course lookup, an insert returning a course, and a manually mapped tutor query.
What it assumes
Assumes familiarity with Rust structs, SQL queries, and database-access functions; shows the relevant `FromRow` derive.
What you can do after
Apply it via SQLx to return `Course` structs from database queries and choose manual mapping when fields do not align.
How it connects
Connects SQLx `FromRow`, PostgreSQL `RETURNING`, optional fetches, and Rust struct-field mapping.

Related concepts

This explainer was generated by Manning's knowledge graph from the text of Rust Servers, Services, and Apps. The section links point to the parts of the book it draws on.