use crate::common::PostgresSchemaType;
use crate::helpers;
use crate::traits::PostgresTable;
use crate::values::PostgresValue;
use core::fmt::Debug;
use core::marker::PhantomData;
use drizzle_core::ToSQL;
use drizzle_core::traits::SQLTable;
use paste::paste;
use super::ExecutableState;
#[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;
#[derive(Debug, Clone, Copy, Default)]
pub struct SelectForSet;
#[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);
join_using_impl!(left, drizzle_core::AfterLeftJoin);
join_using_impl!(left_outer, drizzle_core::AfterLeftJoin);
join_using_impl!(right, drizzle_core::AfterRightJoin);
join_using_impl!(right_outer, drizzle_core::AfterRightJoin);
join_using_impl!(full, drizzle_core::AfterFullJoin);
join_using_impl!(full_outer, drizzle_core::AfterFullJoin);
join_using_impl!(inner, drizzle_core::AfterJoin);
join_using_impl!(); };
($type:ident, $join_expr:expr, $join_trait:path) => {
paste! {
pub fn [<$type _join>]<J: crate::helpers::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,
}
}
}
};
}
macro_rules! join_using_impl {
() => {
pub fn join_using<U: PostgresTable<'a>>(
self,
table: U,
columns: impl ToSQL<'a, PostgresValue<'a>>,
) -> SelectBuilder<
'a,
S,
SelectJoinSet,
U,
<M as drizzle_core::ScopePush<U>>::Out,
<M as drizzle_core::AfterJoin<R, U>>::NewRow,
G,
>
where
M: drizzle_core::AfterJoin<R, U> + drizzle_core::ScopePush<U>,
{
SelectBuilder {
sql: self.sql.append(helpers::join_using(table, columns)),
schema: PhantomData,
state: PhantomData,
table: PhantomData,
marker: PhantomData,
row: PhantomData,
grouped: PhantomData,
}
}
};
($type:ident, $join_trait:path) => {
paste! {
pub fn [<$type _join_using>]<U: PostgresTable<'a>>(
self,
table: U,
columns: impl ToSQL<'a, PostgresValue<'a>>,
) -> SelectBuilder<
'a,
S,
SelectJoinSet,
U,
<M as drizzle_core::ScopePush<U>>::Out,
<M as $join_trait<R, U>>::NewRow,
G,
>
where
M: $join_trait<R, U> + drizzle_core::ScopePush<U>,
{
SelectBuilder {
sql: self.sql.append(helpers::[<$type _join_using>](table, columns)),
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 ExecutableState for SelectForSet {}
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, PostgresValue<'a>>,
M: drizzle_core::ResolveRow<T>,
{
SelectBuilder {
sql: self.sql.append(helpers::from(query)),
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: crate::helpers::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>,
{
use drizzle_core::Join;
SelectBuilder {
sql: self.sql.append(arg.into_join_sql(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, PostgresValue<'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, PostgresValue<'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, PostgresValue<'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: ToSQL<'a, PostgresValue<'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]
pub fn limit(self, limit: usize) -> SelectBuilder<'a, S, SelectLimitSet, T, M, R, G> {
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]
pub fn offset(self, offset: usize) -> SelectBuilder<'a, S, SelectOffsetSet, T, M, R, G> {
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, PostgresSchemaType, PostgresValue<'a>>,
{
#[inline]
#[must_use]
pub fn into_cte<Tag: drizzle_core::Tag + 'static>(
self,
) -> super::CTEView<
'a,
<T as SQLTable<'a, PostgresSchemaType, PostgresValue<'a>>>::Aliased<Tag>,
Self,
> {
let name = Tag::NAME;
super::CTEView::new(
<T as SQLTable<'a, PostgresSchemaType, PostgresValue<'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, PostgresValue<'a>>
for SelectBuilder<'a, S, State, T, M, R, G>
where
State: ExecutableState,
M: drizzle_core::expr::SubqueryType<'a, PostgresValue<'a>>,
{
type SQLType = <M as drizzle_core::expr::SubqueryType<'a, PostgresValue<'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,
}
}
}
pub trait ForLockableState {}
impl ForLockableState for SelectFromSet {}
impl ForLockableState for SelectWhereSet {}
impl ForLockableState for SelectOrderSet {}
impl ForLockableState for SelectLimitSet {}
impl ForLockableState for SelectOffsetSet {}
impl ForLockableState for SelectJoinSet {}
impl ForLockableState for SelectGroupSet {}
impl<'a, S, State, T, M, R, G> SelectBuilder<'a, S, State, T, M, R, G>
where
State: ForLockableState,
{
#[must_use]
pub fn for_update(self) -> SelectBuilder<'a, S, SelectForSet, T, M, R, G> {
SelectBuilder {
sql: self.sql.append(helpers::for_update()),
schema: PhantomData,
state: PhantomData,
table: PhantomData,
marker: PhantomData,
row: PhantomData,
grouped: PhantomData,
}
}
#[must_use]
pub fn for_share(self) -> SelectBuilder<'a, S, SelectForSet, T, M, R, G> {
SelectBuilder {
sql: self.sql.append(helpers::for_share()),
schema: PhantomData,
state: PhantomData,
table: PhantomData,
marker: PhantomData,
row: PhantomData,
grouped: PhantomData,
}
}
#[must_use]
pub fn for_no_key_update(self) -> SelectBuilder<'a, S, SelectForSet, T, M, R, G> {
SelectBuilder {
sql: self.sql.append(helpers::for_no_key_update()),
schema: PhantomData,
state: PhantomData,
table: PhantomData,
marker: PhantomData,
row: PhantomData,
grouped: PhantomData,
}
}
#[must_use]
pub fn for_key_share(self) -> SelectBuilder<'a, S, SelectForSet, T, M, R, G> {
SelectBuilder {
sql: self.sql.append(helpers::for_key_share()),
schema: PhantomData,
state: PhantomData,
table: PhantomData,
marker: PhantomData,
row: PhantomData,
grouped: PhantomData,
}
}
pub fn for_update_of<U: PostgresTable<'a>>(
self,
table: U,
) -> SelectBuilder<'a, S, SelectForSet, T, M, R, G> {
SelectBuilder {
sql: self.sql.append(helpers::for_update_of(table.name())),
schema: PhantomData,
state: PhantomData,
table: PhantomData,
marker: PhantomData,
row: PhantomData,
grouped: PhantomData,
}
}
pub fn for_share_of<U: PostgresTable<'a>>(
self,
table: U,
) -> SelectBuilder<'a, S, SelectForSet, T, M, R, G> {
SelectBuilder {
sql: self.sql.append(helpers::for_share_of(table.name())),
schema: PhantomData,
state: PhantomData,
table: PhantomData,
marker: PhantomData,
row: PhantomData,
grouped: PhantomData,
}
}
}
impl<S, T, M, R, G> SelectBuilder<'_, S, SelectForSet, T, M, R, G> {
#[must_use]
pub fn nowait(self) -> Self {
SelectBuilder {
sql: self.sql.append(helpers::nowait()),
schema: PhantomData,
state: PhantomData,
table: PhantomData,
marker: PhantomData,
row: PhantomData,
grouped: PhantomData,
}
}
#[must_use]
pub fn skip_locked(self) -> Self {
SelectBuilder {
sql: self.sql.append(helpers::skip_locked()),
schema: PhantomData,
state: PhantomData,
table: PhantomData,
marker: PhantomData,
row: PhantomData,
grouped: PhantomData,
}
}
}
#[cfg(test)]
mod tests {
use super::*;
use drizzle_core::{SQL, ToSQL};
#[test]
fn test_select_builder_creation() {
let builder = SelectBuilder::<(), SelectInitial> {
sql: SQL::raw("SELECT *"),
schema: PhantomData,
state: PhantomData,
table: PhantomData,
marker: PhantomData,
row: PhantomData,
grouped: PhantomData,
};
assert_eq!(builder.to_sql().sql(), "SELECT *");
}
}