use std::fmt;
use sqlx::postgres::PgArguments;
use crate::Params;
use crate::Result;
#[derive(Clone, Debug)]
#[must_use]
#[non_exhaustive]
pub struct BuiltQuery {
pub sql: String,
pub params: Params,
pub cacheable: bool,
}
impl BuiltQuery {
pub fn arguments(&self) -> Result<PgArguments> {
self.params.arguments()
}
pub fn pretty(&self) -> PrettyQuery<'_> {
pretty_query(self)
}
}
#[must_use]
pub struct PrettyQuery<'a> {
query: &'a BuiltQuery,
}
impl fmt::Display for PrettyQuery<'_> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
writeln!(f, "SQL:")?;
writeln!(f, "{}", self.query.sql)?;
writeln!(f)?;
let params = self.query.params.as_slice();
if params.is_empty() {
writeln!(f, "Params: none")?;
} else {
writeln!(f, "Params ({}):", params.len())?;
for (index, param) in params.iter().enumerate() {
writeln!(f, "${}: {}", index + 1, param.debug_name())?;
}
}
writeln!(f)?;
write!(f, "Cacheable: {}", self.query.cacheable)
}
}
#[inline]
pub fn pretty_query(query: &BuiltQuery) -> PrettyQuery<'_> {
PrettyQuery { query }
}
#[cfg(test)]
mod tests {
use crate::{BuiltQuery, Param, Params, pretty_query};
#[test]
fn built_query_arguments_delegate_to_stored_params() {
let built = BuiltQuery {
sql: "select $1".to_owned(),
params: Params::from_vec(vec![Param::typed(1_i32)]),
cacheable: true,
};
built.arguments().unwrap();
}
#[test]
fn built_query_clone_keeps_sql_params_and_cacheability() {
let built = BuiltQuery {
sql: "select $1".to_owned(),
params: Params::from_vec(vec![Param::typed("x".to_owned())]),
cacheable: false,
};
let cloned = built.clone();
assert_eq!(cloned.sql, "select $1");
assert_eq!(cloned.params.len(), 1);
assert!(!cloned.cacheable);
}
#[test]
fn pretty_query_formats_sql_params_and_cacheability() {
let built = BuiltQuery {
sql: "select $1, $2".to_owned(),
params: Params::from_vec(vec![Param::typed(1_i32), Param::typed("x".to_owned())]),
cacheable: false,
};
let rendered = pretty_query(&built).to_string();
assert_eq!(
rendered,
format!(
concat!(
"SQL:\n",
"select $1, $2\n",
"\n",
"Params (2):\n",
"$1: {}\n",
"$2: {}\n",
"\n",
"Cacheable: false"
),
std::any::type_name::<i32>(),
std::any::type_name::<String>()
)
);
assert_eq!(built.pretty().to_string(), rendered);
}
#[test]
fn pretty_query_formats_empty_params() {
let built = BuiltQuery {
sql: "select 1".to_owned(),
params: Params::new(),
cacheable: true,
};
assert_eq!(
built.pretty().to_string(),
concat!(
"SQL:\n",
"select 1\n",
"\n",
"Params: none\n",
"\n",
"Cacheable: true"
)
);
}
#[test]
fn built_query_stream_methods_type_check() {
fn assert_type_checks(pool: sqlx::PgPool, built: BuiltQuery) {
let rows = built.fetch_stream(&pool).unwrap();
drop(rows);
let typed_rows = built.fetch_stream_as::<(i64,)>(&pool).unwrap();
drop(typed_rows);
let scalars = built.fetch_stream_scalar::<i64>(&pool).unwrap();
drop(scalars);
let owned_rows = built.clone().fetch_stream_pool(pool.clone()).unwrap();
drop(owned_rows);
let owned_typed_rows = built
.clone()
.fetch_stream_pool_as::<(i64,)>(pool.clone())
.unwrap();
drop(owned_typed_rows);
let owned_scalars = built.fetch_stream_pool_scalar::<i64>(pool).unwrap();
drop(owned_scalars);
}
let _ = assert_type_checks as fn(sqlx::PgPool, BuiltQuery);
}
}