use crate::core::{AggregateExpr, AggregateQuery, Expr, Model, QueryError, SqlValue, WhereExpr, F};
use crate::query::{QuerySet, Q};
use crate::sql::executor::{
fetch_aggregate_pool, FetcherPool as _, MaybeMyFromRow, MaybeMyLoadRelated, MaybePgFromRow,
MaybeSqliteFromRow, MaybeSqliteLoadRelated, UpdaterPool as _,
};
use crate::sql::{ExecError, LoadRelated, Pool};
pub fn resolve_col<T: Model>(col: &str) -> Result<&'static str, ExecError> {
Ok(T::SCHEMA
.field(col)
.ok_or_else(|| {
ExecError::Query(QueryError::UnknownField {
model: T::SCHEMA.name,
field: col.to_string(),
})
})?
.column)
}
#[must_use]
pub fn add_signed_expr(col_static: &'static str, signed_by: i64) -> Expr {
F(col_static) + Expr::Literal(SqlValue::I64(signed_by))
}
pub async fn increment_one_pool<T>(
pk_field: &'static str,
this_pk: SqlValue,
col: &str,
by: i64,
pool: &Pool,
) -> Result<u64, ExecError>
where
T: Model
+ MaybePgFromRow
+ MaybeMyFromRow
+ MaybeSqliteFromRow
+ LoadRelated
+ MaybeMyLoadRelated
+ MaybeSqliteLoadRelated
+ Send
+ Unpin,
{
let col_static = resolve_col::<T>(col)?;
QuerySet::<T>::default()
.filter(pk_field, this_pk)
.update()
.set_expr(col, add_signed_expr(col_static, by))
.execute_pool(pool)
.await
}
pub async fn increment_all_pool<T>(col: &str, by: i64, pool: &Pool) -> Result<u64, ExecError>
where
T: Model
+ MaybePgFromRow
+ MaybeMyFromRow
+ MaybeSqliteFromRow
+ LoadRelated
+ MaybeMyLoadRelated
+ MaybeSqliteLoadRelated
+ Send
+ Unpin,
{
let col_static = resolve_col::<T>(col)?;
QuerySet::<T>::default()
.update()
.set_expr(col, add_signed_expr(col_static, by))
.execute_pool(pool)
.await
}
pub async fn aggregate_one_pool<T, U>(
col: &str,
build: fn(&'static str) -> AggregateExpr,
pool: &Pool,
) -> Result<Option<U>, ExecError>
where
T: Model,
(Option<U>,): MaybePgFromRow + MaybeMyFromRow + MaybeSqliteFromRow + Send + Unpin,
{
let col_static = resolve_col::<T>(col)?;
let q = AggregateQuery {
model: T::SCHEMA,
joins: Vec::new(),
where_clause: WhereExpr::And(Vec::new()),
group_by: Vec::new(),
aggregates: vec![("v".into(), build(col_static))],
aliases: Vec::new(),
having: None,
order_by: Vec::new(),
limit: None,
offset: None,
};
let rows: Vec<(Option<U>,)> = fetch_aggregate_pool(pool, &q).await?;
Ok(rows.into_iter().next().and_then(|t| t.0))
}
pub async fn where_multi_pool<T>(
cols: &[&str],
val: impl Into<SqlValue>,
all: bool,
pool: &Pool,
) -> Result<Vec<T>, ExecError>
where
T: Model
+ MaybePgFromRow
+ MaybeMyFromRow
+ MaybeSqliteFromRow
+ LoadRelated
+ MaybeMyLoadRelated
+ MaybeSqliteLoadRelated
+ Send
+ Unpin,
{
if cols.is_empty() {
return if all {
QuerySet::<T>::default().fetch(pool).await
} else {
Ok(Vec::new())
};
}
let sql_val: SqlValue = val.into();
let mut q: Option<Q> = None;
for col in cols {
let col_static = resolve_col::<T>(col)?;
let pred = Q::eq(col_static, sql_val.clone());
q = Some(match q {
None => pred,
Some(prev) => {
if all {
prev & pred
} else {
prev | pred
}
}
});
}
QuerySet::<T>::default()
.where_(q.expect("non-empty cols"))
.fetch(pool)
.await
}