use std::marker::PhantomData;
use crate::dialect::render_union;
use crate::executor::Executor;
use crate::model::{FromRow, Model};
use crate::query::ast::{OrderTerm, UnionStatement};
use crate::query::queryset::QuerySet;
pub struct UnionQuery<M: Model> {
statement: UnionStatement,
_marker: PhantomData<fn() -> M>,
}
impl<M: Model> UnionQuery<M> {
pub(crate) fn new(first: QuerySet<M>, second: QuerySet<M>, all: bool) -> Self {
Self {
statement: UnionStatement {
first: first.into_statement(),
rest: vec![(all, second.into_statement())],
order_by: Vec::new(),
limit: None,
offset: None,
lock: None,
},
_marker: PhantomData,
}
}
pub fn into_statement(self) -> UnionStatement {
self.statement
}
pub fn union(mut self, other: QuerySet<M>) -> Self {
self.statement.rest.push((false, other.into_statement()));
self
}
pub fn union_all(mut self, other: QuerySet<M>) -> Self {
self.statement.rest.push((true, other.into_statement()));
self
}
pub fn order_by(mut self, term: OrderTerm) -> Self {
self.statement.order_by.push(term);
self
}
pub fn limit(mut self, limit: u64) -> Self {
self.statement.limit = Some(limit);
self
}
pub fn offset(mut self, offset: u64) -> Self {
self.statement.offset = Some(offset);
self
}
pub async fn all(self, executor: impl Executor) -> crate::Result<Vec<M>> {
self.all_as::<M>(executor).await
}
pub async fn all_as<T: FromRow>(self, executor: impl Executor) -> crate::Result<Vec<T>> {
crate::query::queryset::validate_union_for_dialect(executor.dialect(), &self.statement)?;
let (sql, params) = render_union(executor.dialect(), &self.statement);
let rows = executor.fetch_all(sql, params).await?;
rows.iter().map(T::from_row).collect()
}
pub async fn first<E: Executor>(mut self, executor: E) -> crate::Result<Option<M>> {
self.statement.limit = Some(1);
crate::query::queryset::validate_union_for_dialect(executor.dialect(), &self.statement)?;
let (sql, params) = render_union(executor.dialect(), &self.statement);
let rows = executor.fetch_all(sql, params).await?;
match rows.first() {
Some(row) => M::from_row(row).map(Some),
None => Ok(None),
}
}
pub async fn count<E: Executor>(self, executor: E) -> crate::Result<i64> {
crate::query::queryset::validate_union_for_dialect(executor.dialect(), &self.statement)?;
let (inner_sql, params) = render_union(executor.dialect(), &self.statement);
let sql = format!("SELECT COUNT(*) FROM ({inner_sql}) AS _u");
let rows = executor.fetch_all(sql, params).await?;
match rows.first() {
Some(row) => row.get_index::<i64>(0),
None => Ok(0),
}
}
}