Skip to main content

query

Macro query 

Source
query!() { /* proc-macro */ }
Expand description

Type-safe SQL macro that builds and executes a query with compile-time checks.

Validates table/column names, binds arguments, and executes immediately, returns awaitable anyhow::Result.

Notes:

  • For INSERT/UPDATE/DELETE without RETURNING, the output is driver-specific (generally the number of rows affected).
  • Input syntax highlighting is applied in IDE’s, but on documentation page it is not.

§Syntax

query!(<Driver> conn, SQL)
  • <Driver> is optional, if omitted the driver is inferred from conn.
  • conn is generally a connection or transaction implementing EasyExecutor, implemented for mutable versions of both, also implemented for Sqlx Executor types.

Example:

let value = 42;
let result =
    query!(<Sqlite> &mut conn, SELECT OutputType From TableType where column = {value}).await?;
  • SQL keywords are case-insensitive.
  • {value} inserts an external Rust value as a bound argument.
  • See below for more info about the accepted SQL.

§Query forms

§SELECT

SELECT OutputType FROM TableType ... returns OutputType, a single row implementing Output. The Output trait also covers Vec<T> and Option<T> for multiple or optional rows.

let id = 1;
let one: ExprTestData = query!(&mut conn,
    SELECT ExprTestData FROM ExprTestTable WHERE ExprTestTable.id = {id}
)
.await?;
let many: Vec<ExprTestData> = query!(&mut conn,
    SELECT Vec<ExprTestData> FROM ExprTestTable WHERE ExprTestTable.int_field > 0
)
.await?;
let maybe: Option<ExprTestData> = query!(&mut conn,
    SELECT Option<ExprTestData> FROM ExprTestTable WHERE ExprTestTable.id < {id+1}
)
.await?;

Use Table.column or OutputType.column for column references. With the use_output_columns feature, bare column names are validated against the output type instead of the Table type.

let min_val = 10;
let results: Vec<ExprTestData> = query!(&mut conn,
    SELECT Vec<ExprTestData> FROM ExprTestTable
    WHERE ExprTestTable.int_field > {min_val} AND ExprTestData.bool_field = true
    ORDER BY int_field DESC
)
.await?;

Accepted Clauses: WHERE, GROUP BY, HAVING, ORDER BY, LIMIT, DISTINCT

§INSERT

INSERT INTO TableType VALUES {data} inserts one value or a collection. {data} must implement Insert.

let data = ExprTestData {
    int_field: 42,
    str_field: "ready".to_string(),
    bool_field: true,
    nullable_field: None,
};
query!(&mut conn, INSERT INTO ExprTestTable VALUES {data}).await?;

Use RETURNING OutputType to return inserted data, OutputType needs to implement Output.

let data = ExprTestData {
    int_field: 7,
    str_field: "added".to_string(),
    bool_field: false,
    nullable_field: None,
};
let inserted: ExprTestData = query!(&mut conn,
    INSERT INTO ExprTestTable VALUES {data} RETURNING ExprTestData
)
.await?;

§UPDATE

UPDATE TableType SET {update} uses a struct implementing Update, or SET field = value for inline updates.

let id = 1;
let updated = ExprTestData {
    int_field: 5,
    str_field: "updated".to_string(),
    bool_field: false,
    nullable_field: None,
};
query!(&mut conn,
    UPDATE ExprTestTable SET {updated} WHERE id = {id}
)
.await?;
let id = 1;
query!(&mut conn,
    UPDATE ExprTestTable SET str_field = "inline", bool_field = false WHERE ExprTestTable.id = {id}
)
.await?;

Use RETURNING OutputType to return rows, OutputType needs to implement Output.

let bool_value = false;

let updated: Vec<ExprTestData> = query!(&mut conn,
    UPDATE ExprTestTable SET bool_field = {bool_value}
    WHERE ExprTestTable.id > 1
    RETURNING Vec<ExprTestData>
)
.await?;

Accepted Clauses: WHERE, RETURNING

§DELETE

DELETE FROM TableType ... removes rows.

query!(&mut conn, DELETE FROM ExprTestTable WHERE ExprTestTable.id = 1).await?;

Use RETURNING OutputType to return rows, OutputType needs to implement Output

let removed: Option<ExprTestData> = query!(&mut conn,
    DELETE FROM ExprTestTable WHERE ExprTestTable.id = 1 RETURNING Option<ExprTestData>
)
.await?;

Accepted Clauses: WHERE, RETURNING

§EXISTS

EXISTS TableType WHERE ... returns bool.

let exists: bool = query!(&mut conn,
    EXISTS ExprTestTable WHERE str_field IS NOT NULL And str_field = "exists"
)
.await?;

Accepted Clauses: WHERE, GROUP BY, HAVING, ORDER BY, LIMIT

§Table joins

Use table_join! to define joins, then reference joined columns with JoinedTable.column.

table_join!(ExprWithRelated | ExprTestTable INNER JOIN RelatedTestTable ON ExprTestTable.id = RelatedTestTable.parent_id);

#[derive(Output)]
#[sql(table = ExprWithRelated)]
struct ExprRelatedOutput {
    #[sql(field = ExprTestTable.int_field)]
    int_field: i32,
    #[sql(field = RelatedTestTable.data)]
    data: String,
}

let parent_id = 1;
let rows: Vec<ExprRelatedOutput> = query!(&mut conn,
    SELECT Vec<ExprRelatedOutput> FROM ExprWithRelated
    WHERE RelatedTestTable.parent_id = {parent_id}
)
.await?;

§SQL functions

Built-in functions (like COUNT, SUM, LOWER) are available, and custom ones can be registered with custom_sql_function!.

let rows: Vec<ExprTestData> = query!(&mut conn,
    SELECT Vec<ExprTestData> FROM ExprTestTable WHERE LOWER(str_field) = "hello"
)
.await?;
custom_sql_function!(Len; "LENGTH"; 1);
let rows: Vec<ExprTestData> = query!(&mut conn,
    SELECT Vec<ExprTestData> FROM ExprTestTable
    WHERE Len(str_field) > 3
)
.await?;

§IN {vec} parameter binding

IN {vec} expands placeholders at runtime. The value must implement IntoIterator and len() (e.g., Vec<T>, &[T]). Use IN {&vec} if you need to reuse the collection.

let ids = vec![10, 30, 50];
let results: Vec<ExprTestData> = query!(&mut conn,
    SELECT Vec<ExprTestData> FROM ExprTestTable
    WHERE ExprTestTable.int_field IN {ids}
)
.await?;

§Generic connection

*conn syntax might be needed when using &mut EasyExecutor<D> as connection

let rows: Vec<ExprTestData> =
    query!(*conn, SELECT Vec<ExprTestData> FROM ExprTestTable).await?;