use crate::backend::Backend;
use crate::expression::{AsExpression, ValidGrouping};
use crate::query_builder::{AstPass, NotSpecialized, QueryFragment, QueryId};
use crate::sql_types::Bool;
use crate::{AppearsOnTable, Expression, QueryResult, SelectableExpression};
macro_rules! empty_clause {
($name: ident) => {
#[derive(Debug, Clone, Copy, QueryId)]
pub struct $name;
impl<DB> crate::query_builder::QueryFragment<DB> for $name
where
DB: crate::backend::Backend + crate::backend::DieselReserveSpecialization,
{
fn walk_ast<'b>(
&'b self,
_pass: crate::query_builder::AstPass<'_, 'b, DB>,
) -> crate::QueryResult<()> {
Ok(())
}
}
};
}
mod aggregate_filter;
mod aggregate_order;
pub(crate) mod frame_clause;
mod over_clause;
mod partition_by;
mod prefix;
use self::aggregate_filter::{FilterDsl, NoFilter};
pub use self::aggregate_order::Order;
use self::aggregate_order::{NoOrder, OrderAggregateDsl, OrderWindowDsl};
use self::frame_clause::{FrameDsl, NoFrame};
pub use self::over_clause::OverClause;
use self::over_clause::{NoWindow, OverDsl};
use self::partition_by::PartitionByDsl;
use self::prefix::{AllDsl, DistinctDsl, NoPrefix};
#[derive(QueryId, Debug)]
pub struct AggregateExpression<
Fn,
Prefix = NoPrefix,
Order = NoOrder,
Filter = NoFilter,
Window = NoWindow,
> {
prefix: Prefix,
function: Fn,
order: Order,
filter: Filter,
window: Window,
}
impl<Fn, Prefix, Order, Filter, Window, DB> QueryFragment<DB>
for AggregateExpression<Fn, Prefix, Order, Filter, Window>
where
DB: crate::backend::Backend + crate::backend::DieselReserveSpecialization,
Fn: FunctionFragment<DB>,
Prefix: QueryFragment<DB>,
Order: QueryFragment<DB>,
Filter: QueryFragment<DB>,
Window: QueryFragment<DB> + WindowFunctionFragment<Fn, DB>,
{
fn walk_ast<'b>(&'b self, mut pass: AstPass<'_, 'b, DB>) -> QueryResult<()> {
pass.push_sql(Fn::FUNCTION_NAME);
pass.push_sql("(");
self.prefix.walk_ast(pass.reborrow())?;
self.function.walk_arguments(pass.reborrow())?;
self.order.walk_ast(pass.reborrow())?;
pass.push_sql(")");
self.filter.walk_ast(pass.reborrow())?;
self.window.walk_ast(pass.reborrow())?;
Ok(())
}
}
impl<Fn, Prefix, Order, Filter, GB> ValidGrouping<GB>
for AggregateExpression<Fn, Prefix, Order, Filter>
where
Fn: ValidGrouping<GB>,
{
type IsAggregate = <Fn as ValidGrouping<GB>>::IsAggregate;
}
impl<Fn, Prefix, Order, Filter, GB, Partition, WindowOrder, Frame> ValidGrouping<GB>
for AggregateExpression<Fn, Prefix, Order, Filter, OverClause<Partition, WindowOrder, Frame>>
where
Fn: IsWindowFunction,
Fn::ArgTypes: ValidGrouping<GB>,
{
type IsAggregate = <Fn::ArgTypes as ValidGrouping<GB>>::IsAggregate;
}
impl<Fn, Prefix, Order, Filter, Window> Expression
for AggregateExpression<Fn, Prefix, Order, Filter, Window>
where
Fn: Expression,
{
type SqlType = <Fn as Expression>::SqlType;
}
impl<Fn, Prefix, Order, Filter, Window, QS> AppearsOnTable<QS>
for AggregateExpression<Fn, Prefix, Order, Filter, Window>
where
Self: Expression,
Fn: AppearsOnTable<QS>,
{
}
impl<Fn, Prefix, Order, Filter, Window, QS> SelectableExpression<QS>
for AggregateExpression<Fn, Prefix, Order, Filter, Window>
where
Self: Expression,
Fn: SelectableExpression<QS>,
{
}
#[diagnostic::on_unimplemented(
message = "`{Self}` is not a window function",
label = "remove this function call to use `{Self}` as normal SQL function",
note = "try removing any method call to `WindowExpressionMethods` and use it as normal SQL function"
)]
pub trait IsWindowFunction {
type ArgTypes;
}
pub trait WindowFunctionFragment<Fn, DB: Backend, SP = NotSpecialized> {}
pub trait IsAggregateFunction {}
pub trait FunctionFragment<DB: Backend> {
const FUNCTION_NAME: &'static str;
fn walk_arguments<'b>(&'b self, pass: AstPass<'_, 'b, DB>) -> QueryResult<()>;
}
pub trait AggregateExpressionMethods: Sized {
fn aggregate_distinct(self) -> self::dsl::AggregateDistinct<Self>
where
Self: DistinctDsl,
{
<Self as DistinctDsl>::distinct(self)
}
fn aggregate_all(self) -> self::dsl::AggregateAll<Self>
where
Self: AllDsl,
{
<Self as AllDsl>::all(self)
}
fn aggregate_filter<P>(self, f: P) -> self::dsl::AggregateFilter<Self, P>
where
P: AsExpression<Bool>,
Self: FilterDsl<P::Expression>,
{
<Self as FilterDsl<P::Expression>>::filter(self, f.as_expression())
}
fn aggregate_order<O>(self, o: O) -> self::dsl::AggregateOrder<Self, O>
where
Self: OrderAggregateDsl<O>,
{
<Self as OrderAggregateDsl<O>>::order(self, o)
}
}
impl<T> AggregateExpressionMethods for T {}
pub trait WindowExpressionMethods: Sized {
fn over(self) -> self::dsl::Over<Self>
where
Self: OverDsl,
{
<Self as OverDsl>::over(self)
}
fn window_filter<P>(self, f: P) -> self::dsl::WindowFilter<Self, P>
where
P: AsExpression<Bool>,
Self: FilterDsl<P::Expression>,
{
<Self as FilterDsl<P::Expression>>::filter(self, f.as_expression())
}
fn partition_by<E>(self, expr: E) -> self::dsl::PartitionBy<Self, E>
where
Self: PartitionByDsl<E>,
{
<Self as PartitionByDsl<E>>::partition_by(self, expr)
}
fn window_order<E>(self, expr: E) -> self::dsl::WindowOrder<Self, E>
where
Self: OrderWindowDsl<E>,
{
<Self as OrderWindowDsl<E>>::order(self, expr)
}
fn frame_by<E>(self, expr: E) -> self::dsl::FrameBy<Self, E>
where
Self: FrameDsl<E>,
{
<Self as FrameDsl<E>>::frame(self, expr)
}
}
impl<T> WindowExpressionMethods for T {}
pub(super) mod dsl {
#[cfg(doc)]
use super::frame_clause::{FrameBoundDsl, FrameClauseDsl};
use super::*;
pub type Over<Fn> = <Fn as OverDsl>::Output;
pub type WindowFilter<Fn, P> = <Fn as FilterDsl<crate::dsl::AsExprOf<P, Bool>>>::Output;
pub type PartitionBy<Fn, E> = <Fn as PartitionByDsl<E>>::Output;
pub type WindowOrder<Fn, E> = <Fn as OrderWindowDsl<E>>::Output;
pub type FrameBy<Fn, E> = <Fn as FrameDsl<E>>::Output;
pub type AggregateDistinct<Fn> = <Fn as DistinctDsl>::Output;
pub type AggregateAll<Fn> = <Fn as AllDsl>::Output;
pub type AggregateFilter<Fn, P> = <Fn as FilterDsl<crate::dsl::AsExprOf<P, Bool>>>::Output;
pub type AggregateOrder<Fn, O> = <Fn as OrderAggregateDsl<O>>::Output;
pub type FrameStartWith<S, T> = self::frame_clause::StartFrame<S, T>;
pub type FrameStartWithExclusion<S, T, E> = self::frame_clause::StartFrame<S, T, E>;
pub type FrameBetween<S, E1, E2> = self::frame_clause::BetweenFrame<S, E1, E2>;
pub type FrameBetweenWithExclusion<S, E1, E2, E> =
self::frame_clause::BetweenFrame<S, E1, E2, E>;
pub type Preceding<I> = self::frame_clause::OffsetPreceding<I>;
pub type Following<I> = self::frame_clause::OffsetFollowing<I>;
}