use crate::helpers::{self, JoinArg};
use crate::values::SQLiteValue;
use core::marker::PhantomData;
use drizzle_core::{SQLTable, ToSQL};
use paste::paste;
pub use drizzle_core::builder::{
AsCteState, SelectFromSet, SelectGroupSet, SelectInitial, SelectJoinSet, SelectLimitSet,
SelectOffsetSet, SelectOrderSet, SelectSetOpSet, SelectWhereSet,
};
#[doc(hidden)]
pub trait SelectWhereAllowed: drizzle_core::WhereAllowed {}
impl SelectWhereAllowed for SelectFromSet {}
impl SelectWhereAllowed for SelectJoinSet {}
#[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: self.sql.append(arg.into_join_sql($join_expr)),
schema: PhantomData,
state: PhantomData,
table: PhantomData,
marker: PhantomData,
row: PhantomData,
grouped: PhantomData,
}
}
}
};
}
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 = self.sql.append(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: self
.sql
.append(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: SelectWhereAllowed,
{
#[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: self.sql.append(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: self.sql.append(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: self.sql.append(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: self.sql.append(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: self.sql.append(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: self.sql.append(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: drizzle_core::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: drizzle_core::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: drizzle_core::ExecutableState;
type Table;
fn into_select(self) -> SelectBuilder<'a, S, Self::State, Self::Table, M, R>;
}
impl<'a, S, State: drizzle_core::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,
}
}
}