#[sql_query_many]Expand description
This macro attribute is used to ad-hoc query returning multiple rows from the database.
§Example
use std::convert::Infallible;
use tracing::trace;
use tokio_postgres::Client;
use futures_util::stream::try_stream::TryStreamExt;
#[sql_fun::sql_query_many("select id, name from users where users.name like ${name_like}")]
async fn select_users_by_name_like<F, Fut>(
client: &Client,
name_like: &str,
mut collector: VecCollector<RowType>,
handler: F,
) -> Result<VecCollector<RowType>, anyhow::Error>
where
F: Fn(VecCollector<RowType>, RowType) -> Fut,
Fut: Future<Output = Result<VecCollector<RowType>, anyhow::Error>>,
{
// You can add code before the query executes,
// such as trace logging, permission checks, etc.
//
trace!("Query is about to execute...");
// `sql_query_many` appends the generated query execution code
// to the end of the function body.
// If you return early here, the query will not be executed.
}
///
/// RowType example
///
/// The `derive_builder::Builder` crate satisfies almost requirements.
/// only add `fn builder()-> BuilderType` to fits row type.
///
#[derive(derive_builder::Builder)]
struct RowType {
id: i64,
name: String,
}
impl RowType {
fn builder() -> RowTypeBuilder {
RowTypeBuilder::default()
}
}
/// Collector example
struct VecCollector<T> {
collected: Vec<T>,
}
impl<T> VecCollector<T> {
pub fn new() -> Self {
Self {
collected: Vec::new(),
}
}
/// collect method called from handler
pub async fn collect(mut self, value: T) -> Result<Self, Infallible> {
self.collected.push(value);
Ok(self)
}
pub fn into_inner(mut self) -> Vec<T> {
self.collected.shrink_to_fit();
self.collected
}
}§sql_query_many requirements
- require
asyncfunction. - The first parameter must represent the database connection.
sql_query_manyuses only the parameter’s name during macro expansion.- The parameter may be of any type, such as
Client,Transaction, or a user-defined wrapper type. - As long as the type supports
prepareandquery_rawwith compatible signatures, it can be used as the connection.
- The last two parameters are interpreted as the
collectorand thehandler, in that order.- Their names do not matter; only their position in the function signature is used to determine their role.
- The
handleris called once for each row in the result set. It consumes thecollectorand returns a new one. - The
collectormaintains state across the query execution.- The
collectorparameter must be declared asmut. - Any type can be used as the
collector;sql_query_manydoes not directly call it.- The generated function simply owns and passes the collector.
- The final
collectorvalue is returned by the generated function.
- The
- The type of
handlermust be specified via generic parameters.- The
handlermust be a function or closure that implementsFnand takes two parameters: thecollectorand a row from the result set.sql_query_manyinfers the row type from the generic signature asRinFn(C, R) -> Fut.
- The
handlermust be anasyncfunction and returnResult<collector, error>.
- The
- All other parameters are used for SQL parameter binding.
§Row type requirements
- Must implement a
fn builder() -> BuilderTypemethod to return a builder instance.- The builder must provide setter methods with names matching the SQL column names.
- The builder must implement a
build()method that returns the final row type instance.
§Error Type Requirements
This function returns Result<IdRow, E>, where E must:
- Implement
From<tokio_postgres::Error>(for query execution failures). - Implement
From<std::io::Error>(for unexpected column mismatches in the prepare check statement). - Be capable of handling any errors returned by the builder type.