use crate::helpers::{self, JoinArg};
use crate::values::SQLiteValue;
use core::fmt::Debug;
use core::marker::PhantomData;
use drizzle_core::{SQL, SQLTable, ToSQL};
use paste::paste;
use super::ExecutableState;
#[inline]
fn append_sql<'a>(
mut base: SQL<'a, SQLiteValue<'a>>,
fragment: SQL<'a, SQLiteValue<'a>>,
) -> SQL<'a, SQLiteValue<'a>> {
base.append_mut(fragment);
base
}
#[derive(Debug, Clone, Copy, Default)]
pub struct SelectInitial;
#[derive(Debug, Clone, Copy, Default)]
pub struct SelectFromSet;
#[derive(Debug, Clone, Copy, Default)]
pub struct SelectJoinSet;
#[derive(Debug, Clone, Copy, Default)]
pub struct SelectWhereSet;
#[derive(Debug, Clone, Copy, Default)]
pub struct SelectGroupSet;
#[derive(Debug, Clone, Copy, Default)]
pub struct SelectOrderSet;
#[derive(Debug, Clone, Copy, Default)]
pub struct SelectLimitSet;
#[derive(Debug, Clone, Copy, Default)]
pub struct SelectOffsetSet;
#[derive(Debug, Clone, Copy, Default)]
pub struct SelectSetOpSet;
#[doc(hidden)]
macro_rules! join_impl {
() => {
join_impl!(natural, Join::new().natural(), drizzle_core::AfterJoin);
join_impl!(natural_left, Join::new().natural().left(), drizzle_core::AfterLeftJoin);
join_impl!(left, Join::new().left(), drizzle_core::AfterLeftJoin);
join_impl!(left_outer, Join::new().left().outer(), drizzle_core::AfterLeftJoin);
join_impl!(natural_left_outer, Join::new().natural().left().outer(), drizzle_core::AfterLeftJoin);
join_impl!(natural_right, Join::new().natural().right(), drizzle_core::AfterRightJoin);
join_impl!(right, Join::new().right(), drizzle_core::AfterRightJoin);
join_impl!(right_outer, Join::new().right().outer(), drizzle_core::AfterRightJoin);
join_impl!(natural_right_outer, Join::new().natural().right().outer(), drizzle_core::AfterRightJoin);
join_impl!(natural_full, Join::new().natural().full(), drizzle_core::AfterFullJoin);
join_impl!(full, Join::new().full(), drizzle_core::AfterFullJoin);
join_impl!(full_outer, Join::new().full().outer(), drizzle_core::AfterFullJoin);
join_impl!(natural_full_outer, Join::new().natural().full().outer(), drizzle_core::AfterFullJoin);
join_impl!(inner, Join::new().inner(), drizzle_core::AfterJoin);
join_impl!(cross, Join::new().cross(), drizzle_core::AfterJoin);
};
($type:ident, $join_expr:expr, $join_trait:path) => {
paste! {
#[allow(clippy::type_complexity)]
pub fn [<$type _join>]<J: JoinArg<'a, T>>(
self,
arg: J,
) -> SelectBuilder<'a, S, SelectJoinSet, J::JoinedTable, <M as drizzle_core::ScopePush<J::JoinedTable>>::Out, <M as $join_trait<R, J::JoinedTable>>::NewRow, G>
where
M: $join_trait<R, J::JoinedTable> + drizzle_core::ScopePush<J::JoinedTable>,
{
use drizzle_core::Join;
SelectBuilder {
sql: append_sql(self.sql, arg.into_join_sql($join_expr)),
schema: PhantomData,
state: PhantomData,
table: PhantomData,
marker: PhantomData,
row: PhantomData,
grouped: PhantomData,
}
}
}
};
}
impl ExecutableState for SelectFromSet {}
impl ExecutableState for SelectWhereSet {}
impl ExecutableState for SelectLimitSet {}
impl ExecutableState for SelectOffsetSet {}
impl ExecutableState for SelectOrderSet {}
impl ExecutableState for SelectGroupSet {}
impl ExecutableState for SelectJoinSet {}
impl ExecutableState for SelectSetOpSet {}
impl drizzle_core::WhereAllowed for SelectFromSet {}
impl drizzle_core::WhereAllowed for SelectJoinSet {}
impl drizzle_core::GroupByAllowed for SelectFromSet {}
impl drizzle_core::GroupByAllowed for SelectJoinSet {}
impl drizzle_core::GroupByAllowed for SelectWhereSet {}
impl drizzle_core::OrderByAllowed for SelectFromSet {}
impl drizzle_core::OrderByAllowed for SelectJoinSet {}
impl drizzle_core::OrderByAllowed for SelectWhereSet {}
impl drizzle_core::OrderByAllowed for SelectGroupSet {}
impl drizzle_core::OrderByAllowed for SelectSetOpSet {}
impl drizzle_core::LimitAllowed for SelectFromSet {}
impl drizzle_core::LimitAllowed for SelectJoinSet {}
impl drizzle_core::LimitAllowed for SelectWhereSet {}
impl drizzle_core::LimitAllowed for SelectGroupSet {}
impl drizzle_core::LimitAllowed for SelectOrderSet {}
impl drizzle_core::LimitAllowed for SelectSetOpSet {}
impl drizzle_core::OffsetAllowed for SelectFromSet {}
impl drizzle_core::OffsetAllowed for SelectLimitSet {}
impl drizzle_core::OffsetAllowed for SelectSetOpSet {}
impl drizzle_core::JoinAllowed for SelectFromSet {}
impl drizzle_core::JoinAllowed for SelectJoinSet {}
impl drizzle_core::HavingAllowed for SelectGroupSet {}
impl drizzle_core::GroupByApplied for SelectGroupSet {}
impl drizzle_core::GroupByApplied for SelectOrderSet {}
impl drizzle_core::GroupByApplied for SelectLimitSet {}
impl drizzle_core::GroupByApplied for SelectOffsetSet {}
impl drizzle_core::GroupByApplied for SelectSetOpSet {}
#[doc(hidden)]
pub trait AsCteState {}
impl AsCteState for SelectFromSet {}
impl AsCteState for SelectJoinSet {}
impl AsCteState for SelectWhereSet {}
impl AsCteState for SelectGroupSet {}
impl AsCteState for SelectOrderSet {}
impl AsCteState for SelectLimitSet {}
impl AsCteState for SelectOffsetSet {}
pub type SelectBuilder<'a, Schema, State, Table = (), Marker = (), Row = (), Grouped = ()> =
super::QueryBuilder<'a, Schema, State, Table, Marker, Row, Grouped>;
impl<'a, S, M> SelectBuilder<'a, S, SelectInitial, (), M> {
#[inline]
#[allow(clippy::type_complexity)]
pub fn from<T>(
self,
query: T,
) -> SelectBuilder<
'a,
S,
SelectFromSet,
T,
drizzle_core::Scoped<M, drizzle_core::Cons<T, drizzle_core::Nil>>,
<M as drizzle_core::ResolveRow<T>>::Row,
>
where
T: ToSQL<'a, SQLiteValue<'a>>,
M: drizzle_core::ResolveRow<T>,
{
let sql = append_sql(self.sql, helpers::from(query));
SelectBuilder {
sql,
schema: PhantomData,
state: PhantomData,
table: PhantomData,
marker: PhantomData,
row: PhantomData,
grouped: PhantomData,
}
}
}
impl<'a, S, State, T, M, R, G> SelectBuilder<'a, S, State, T, M, R, G>
where
State: drizzle_core::JoinAllowed,
{
#[inline]
#[allow(clippy::type_complexity)]
pub fn join<J: JoinArg<'a, T>>(
self,
arg: J,
) -> SelectBuilder<
'a,
S,
SelectJoinSet,
J::JoinedTable,
<M as drizzle_core::ScopePush<J::JoinedTable>>::Out,
<M as drizzle_core::AfterJoin<R, J::JoinedTable>>::NewRow,
G,
>
where
M: drizzle_core::AfterJoin<R, J::JoinedTable> + drizzle_core::ScopePush<J::JoinedTable>,
{
SelectBuilder {
sql: append_sql(self.sql, arg.into_join_sql(drizzle_core::Join::new())),
schema: PhantomData,
state: PhantomData,
table: PhantomData,
marker: PhantomData,
row: PhantomData,
grouped: PhantomData,
}
}
join_impl!();
}
impl<'a, S, State, T, M, R, G> SelectBuilder<'a, S, State, T, M, R, G>
where
State: drizzle_core::WhereAllowed,
{
#[inline]
pub fn r#where<E>(self, condition: E) -> SelectBuilder<'a, S, SelectWhereSet, T, M, R, G>
where
E: drizzle_core::expr::Expr<'a, SQLiteValue<'a>>,
E::SQLType: drizzle_core::types::BooleanLike,
{
SelectBuilder {
sql: append_sql(self.sql, helpers::r#where(condition)),
schema: PhantomData,
state: PhantomData,
table: PhantomData,
marker: PhantomData,
row: PhantomData,
grouped: PhantomData,
}
}
}
impl<'a, S, State, T, M, R, G> SelectBuilder<'a, S, State, T, M, R, G>
where
State: drizzle_core::GroupByAllowed,
{
pub fn group_by<Gr>(
self,
columns: Gr,
) -> SelectBuilder<'a, S, SelectGroupSet, T, M, R, Gr::Columns>
where
Gr: drizzle_core::IntoGroupBy<'a, SQLiteValue<'a>>,
{
SelectBuilder {
sql: append_sql(self.sql, helpers::group_by_expr(columns)),
schema: PhantomData,
state: PhantomData,
table: PhantomData,
marker: PhantomData,
row: PhantomData,
grouped: PhantomData,
}
}
}
impl<'a, S, State, T, M, R, G> SelectBuilder<'a, S, State, T, M, R, G>
where
State: drizzle_core::HavingAllowed,
{
pub fn having<E>(self, condition: E) -> SelectBuilder<'a, S, SelectGroupSet, T, M, R, G>
where
E: drizzle_core::expr::Expr<'a, SQLiteValue<'a>>,
E::SQLType: drizzle_core::types::BooleanLike,
{
SelectBuilder {
sql: append_sql(self.sql, helpers::having(condition)),
schema: PhantomData,
state: PhantomData,
table: PhantomData,
marker: PhantomData,
row: PhantomData,
grouped: PhantomData,
}
}
}
impl<'a, S, State, T, M, R, G> SelectBuilder<'a, S, State, T, M, R, G>
where
State: drizzle_core::OrderByAllowed,
{
#[inline]
pub fn order_by<TOrderBy>(
self,
expressions: TOrderBy,
) -> SelectBuilder<'a, S, SelectOrderSet, T, M, R, G>
where
TOrderBy: drizzle_core::ToSQL<'a, SQLiteValue<'a>>,
{
SelectBuilder {
sql: append_sql(self.sql, helpers::order_by(expressions)),
schema: PhantomData,
state: PhantomData,
table: PhantomData,
marker: PhantomData,
row: PhantomData,
grouped: PhantomData,
}
}
}
impl<'a, S, State, T, M, R, G> SelectBuilder<'a, S, State, T, M, R, G>
where
State: drizzle_core::LimitAllowed,
{
#[inline]
#[must_use]
#[track_caller]
pub fn limit<P>(self, limit: P) -> SelectBuilder<'a, S, SelectLimitSet, T, M, R, G>
where
P: drizzle_core::PaginationArg<'a, SQLiteValue<'a>>,
{
SelectBuilder {
sql: append_sql(self.sql, helpers::limit(limit)),
schema: PhantomData,
state: PhantomData,
table: PhantomData,
marker: PhantomData,
row: PhantomData,
grouped: PhantomData,
}
}
}
impl<'a, S, State, T, M, R, G> SelectBuilder<'a, S, State, T, M, R, G>
where
State: drizzle_core::OffsetAllowed,
{
#[inline]
#[must_use]
#[track_caller]
pub fn offset<P>(self, offset: P) -> SelectBuilder<'a, S, SelectOffsetSet, T, M, R, G>
where
P: drizzle_core::PaginationArg<'a, SQLiteValue<'a>>,
{
SelectBuilder {
sql: append_sql(self.sql, helpers::offset(offset)),
schema: PhantomData,
state: PhantomData,
table: PhantomData,
marker: PhantomData,
row: PhantomData,
grouped: PhantomData,
}
}
}
impl<'a, S, State, T, M, R, G> SelectBuilder<'a, S, State, T, M, R, G>
where
State: AsCteState,
T: SQLTable<'a, crate::common::SQLiteSchemaType, SQLiteValue<'a>>,
{
#[inline]
#[must_use]
pub fn into_cte<Tag: drizzle_core::Tag + 'static>(
self,
) -> super::CTEView<
'a,
<T as SQLTable<'a, crate::common::SQLiteSchemaType, SQLiteValue<'a>>>::Aliased<Tag>,
Self,
> {
let name = Tag::NAME;
super::CTEView::new(
<T as SQLTable<'a, crate::common::SQLiteSchemaType, SQLiteValue<'a>>>::alias::<Tag>(),
name,
self,
)
}
}
impl<'a, S, State, T, M, R, G> SelectBuilder<'a, S, State, T, M, R, G>
where
State: ExecutableState,
{
pub fn union(
self,
other: impl IntoSelect<'a, S, M, R>,
) -> SelectBuilder<'a, S, SelectSetOpSet, T, M, R, G> {
SelectBuilder {
sql: helpers::union(self.sql, other.into_select()),
schema: PhantomData,
state: PhantomData,
table: PhantomData,
marker: PhantomData,
row: PhantomData,
grouped: PhantomData,
}
}
pub fn union_all(
self,
other: impl IntoSelect<'a, S, M, R>,
) -> SelectBuilder<'a, S, SelectSetOpSet, T, M, R, G> {
SelectBuilder {
sql: helpers::union_all(self.sql, other.into_select()),
schema: PhantomData,
state: PhantomData,
table: PhantomData,
marker: PhantomData,
row: PhantomData,
grouped: PhantomData,
}
}
pub fn intersect(
self,
other: impl IntoSelect<'a, S, M, R>,
) -> SelectBuilder<'a, S, SelectSetOpSet, T, M, R, G> {
SelectBuilder {
sql: helpers::intersect(self.sql, other.into_select()),
schema: PhantomData,
state: PhantomData,
table: PhantomData,
marker: PhantomData,
row: PhantomData,
grouped: PhantomData,
}
}
pub fn intersect_all(
self,
other: impl IntoSelect<'a, S, M, R>,
) -> SelectBuilder<'a, S, SelectSetOpSet, T, M, R, G> {
SelectBuilder {
sql: helpers::intersect_all(self.sql, other.into_select()),
schema: PhantomData,
state: PhantomData,
table: PhantomData,
marker: PhantomData,
row: PhantomData,
grouped: PhantomData,
}
}
pub fn except(
self,
other: impl IntoSelect<'a, S, M, R>,
) -> SelectBuilder<'a, S, SelectSetOpSet, T, M, R, G> {
SelectBuilder {
sql: helpers::except(self.sql, other.into_select()),
schema: PhantomData,
state: PhantomData,
table: PhantomData,
marker: PhantomData,
row: PhantomData,
grouped: PhantomData,
}
}
pub fn except_all(
self,
other: impl IntoSelect<'a, S, M, R>,
) -> SelectBuilder<'a, S, SelectSetOpSet, T, M, R, G> {
SelectBuilder {
sql: helpers::except_all(self.sql, other.into_select()),
schema: PhantomData,
state: PhantomData,
table: PhantomData,
marker: PhantomData,
row: PhantomData,
grouped: PhantomData,
}
}
}
impl<'a, S, State, T, M, R, G> drizzle_core::expr::Expr<'a, SQLiteValue<'a>>
for SelectBuilder<'a, S, State, T, M, R, G>
where
State: ExecutableState,
M: drizzle_core::expr::SubqueryType<'a, SQLiteValue<'a>>,
{
type SQLType = <M as drizzle_core::expr::SubqueryType<'a, SQLiteValue<'a>>>::SQLType;
type Nullable = drizzle_core::expr::Null;
type Aggregate = drizzle_core::expr::Scalar;
}
pub trait IntoSelect<'a, S, M, R> {
type State: ExecutableState;
type Table;
fn into_select(self) -> SelectBuilder<'a, S, Self::State, Self::Table, M, R>;
}
impl<'a, S, State: ExecutableState, T, M, R, G> IntoSelect<'a, S, M, R>
for SelectBuilder<'a, S, State, T, M, R, G>
{
type State = State;
type Table = T;
fn into_select(self) -> SelectBuilder<'a, S, State, T, M, R> {
SelectBuilder {
sql: self.sql,
schema: PhantomData,
state: PhantomData,
table: PhantomData,
marker: PhantomData,
row: PhantomData,
grouped: PhantomData,
}
}
}