use crate::common::PostgresSchemaType;
use crate::helpers;
use crate::traits::PostgresTable;
use crate::values::PostgresValue;
use drizzle_core::ToSQL;
use drizzle_core::traits::SQLTable;
use paste::paste;
use std::fmt::Debug;
use std::marker::PhantomData;
use super::ExecutableState;
#[derive(Debug, Clone, Copy, Default)]
pub struct SelectInitial;
impl SelectInitial {
#[inline]
pub const fn new() -> Self {
Self
}
}
#[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;
impl SelectFromSet {
#[inline]
pub const fn new() -> Self {
Self
}
}
impl SelectJoinSet {
#[inline]
pub const fn new() -> Self {
Self
}
}
impl SelectWhereSet {
#[inline]
pub const fn new() -> Self {
Self
}
}
impl SelectGroupSet {
#[inline]
pub const fn new() -> Self {
Self
}
}
impl SelectOrderSet {
#[inline]
pub const fn new() -> Self {
Self
}
}
impl SelectLimitSet {
#[inline]
pub const fn new() -> Self {
Self
}
}
impl SelectOffsetSet {
#[inline]
pub const fn new() -> Self {
Self
}
}
impl SelectSetOpSet {
#[inline]
pub const fn new() -> Self {
Self
}
}
impl SelectForSet {
#[inline]
pub const fn new() -> Self {
Self
}
}
#[doc(hidden)]
macro_rules! join_impl {
() => {
join_impl!(natural);
join_impl!(natural_left);
join_impl!(left);
join_impl!(left_outer);
join_impl!(natural_left_outer);
join_impl!(natural_right);
join_impl!(right);
join_impl!(right_outer);
join_impl!(natural_right_outer);
join_impl!(natural_full);
join_impl!(full);
join_impl!(full_outer);
join_impl!(natural_full_outer);
join_impl!(inner);
join_impl!(cross);
join_using_impl!(left);
join_using_impl!(left_outer);
join_using_impl!(right);
join_using_impl!(right_outer);
join_using_impl!(full);
join_using_impl!(full_outer);
join_using_impl!(inner);
join_using_impl!(); };
($type:ident) => {
paste! {
pub fn [<$type _join>]<U: PostgresTable<'a>>(
self,
table: U,
condition: impl ToSQL<'a, PostgresValue<'a>>,
) -> SelectBuilder<'a, S, SelectJoinSet, T> {
SelectBuilder {
sql: self.sql.append(helpers::[<$type _join>](table, condition)),
schema: PhantomData,
state: PhantomData,
table: 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, T> {
SelectBuilder {
sql: self.sql.append(helpers::join_using(table, columns)),
schema: PhantomData,
state: PhantomData,
table: PhantomData,
}
}
};
($type:ident) => {
paste! {
pub fn [<$type _join_using>]<U: PostgresTable<'a>>(
self,
table: U,
columns: impl ToSQL<'a, PostgresValue<'a>>,
) -> SelectBuilder<'a, S, SelectJoinSet, T> {
SelectBuilder {
sql: self.sql.append(helpers::[<$type _join_using>](table, columns)),
schema: PhantomData,
state: PhantomData,
table: 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 {}
#[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 = ()> =
super::QueryBuilder<'a, Schema, State, Table>;
impl<'a, S> SelectBuilder<'a, S, SelectInitial> {
#[inline]
pub fn from<T>(self, query: T) -> SelectBuilder<'a, S, SelectFromSet, T>
where
T: ToSQL<'a, PostgresValue<'a>>,
{
SelectBuilder {
sql: self.sql.append(helpers::from(query)),
schema: PhantomData,
state: PhantomData,
table: PhantomData,
}
}
}
impl<'a, S, T> SelectBuilder<'a, S, SelectFromSet, T> {
#[inline]
pub fn join<U: PostgresTable<'a>>(
self,
table: U,
condition: impl ToSQL<'a, PostgresValue<'a>>,
) -> SelectBuilder<'a, S, SelectJoinSet, T> {
SelectBuilder {
sql: self.sql.append(helpers::join(table, condition)),
schema: PhantomData,
state: PhantomData,
table: PhantomData,
}
}
join_impl!();
#[inline]
pub fn r#where(
self,
condition: impl ToSQL<'a, PostgresValue<'a>>,
) -> SelectBuilder<'a, S, SelectWhereSet, T> {
SelectBuilder {
sql: self.sql.append(helpers::r#where(condition)),
schema: PhantomData,
state: PhantomData,
table: PhantomData,
}
}
pub fn group_by(
self,
expressions: impl IntoIterator<Item = impl ToSQL<'a, PostgresValue<'a>>>,
) -> SelectBuilder<'a, S, SelectGroupSet, T> {
SelectBuilder {
sql: self.sql.append(helpers::group_by(expressions)),
schema: PhantomData,
state: PhantomData,
table: PhantomData,
}
}
#[inline]
pub fn limit(self, limit: usize) -> SelectBuilder<'a, S, SelectLimitSet, T> {
SelectBuilder {
sql: self.sql.append(helpers::limit(limit)),
schema: PhantomData,
state: PhantomData,
table: PhantomData,
}
}
#[inline]
pub fn offset(self, offset: usize) -> SelectBuilder<'a, S, SelectOffsetSet, T> {
SelectBuilder {
sql: self.sql.append(helpers::offset(offset)),
schema: PhantomData,
state: PhantomData,
table: PhantomData,
}
}
#[inline]
pub fn order_by<TOrderBy>(
self,
expressions: TOrderBy,
) -> SelectBuilder<'a, S, SelectOrderSet, T>
where
TOrderBy: ToSQL<'a, PostgresValue<'a>>,
{
SelectBuilder {
sql: self.sql.append(helpers::order_by(expressions)),
schema: PhantomData,
state: PhantomData,
table: PhantomData,
}
}
}
impl<'a, S, T> SelectBuilder<'a, S, SelectJoinSet, T> {
#[inline]
pub fn r#where(
self,
condition: impl ToSQL<'a, PostgresValue<'a>>,
) -> SelectBuilder<'a, S, SelectWhereSet, T> {
SelectBuilder {
sql: self.sql.append(crate::helpers::r#where(condition)),
schema: PhantomData,
state: PhantomData,
table: PhantomData,
}
}
#[inline]
pub fn order_by<TOrderBy>(
self,
expressions: TOrderBy,
) -> SelectBuilder<'a, S, SelectOrderSet, T>
where
TOrderBy: ToSQL<'a, PostgresValue<'a>>,
{
SelectBuilder {
sql: self.sql.append(helpers::order_by(expressions)),
schema: PhantomData,
state: PhantomData,
table: PhantomData,
}
}
#[inline]
pub fn join<U: PostgresTable<'a>>(
self,
table: U,
condition: impl ToSQL<'a, PostgresValue<'a>>,
) -> SelectBuilder<'a, S, SelectJoinSet, T> {
SelectBuilder {
sql: self.sql.append(helpers::join(table, condition)),
schema: PhantomData,
state: PhantomData,
table: PhantomData,
}
}
join_impl!();
}
impl<'a, S, T> SelectBuilder<'a, S, SelectWhereSet, T> {
pub fn group_by(
self,
expressions: impl IntoIterator<Item = impl ToSQL<'a, PostgresValue<'a>>>,
) -> SelectBuilder<'a, S, SelectGroupSet, T> {
SelectBuilder {
sql: self.sql.append(helpers::group_by(expressions)),
schema: PhantomData,
state: PhantomData,
table: PhantomData,
}
}
pub fn order_by<TOrderBy>(
self,
expressions: TOrderBy,
) -> SelectBuilder<'a, S, SelectOrderSet, T>
where
TOrderBy: ToSQL<'a, PostgresValue<'a>>,
{
SelectBuilder {
sql: self.sql.append(helpers::order_by(expressions)),
schema: PhantomData,
state: PhantomData,
table: PhantomData,
}
}
pub fn limit(self, limit: usize) -> SelectBuilder<'a, S, SelectLimitSet, T> {
SelectBuilder {
sql: self.sql.append(helpers::limit(limit)),
schema: PhantomData,
state: PhantomData,
table: PhantomData,
}
}
}
impl<'a, S, T> SelectBuilder<'a, S, SelectGroupSet, T> {
pub fn having(
self,
condition: impl ToSQL<'a, PostgresValue<'a>>,
) -> SelectBuilder<'a, S, SelectGroupSet, T> {
SelectBuilder {
sql: self.sql.append(helpers::having(condition)),
schema: PhantomData,
state: PhantomData,
table: PhantomData,
}
}
pub fn order_by<TOrderBy>(
self,
expressions: TOrderBy,
) -> SelectBuilder<'a, S, SelectOrderSet, T>
where
TOrderBy: ToSQL<'a, PostgresValue<'a>>,
{
SelectBuilder {
sql: self.sql.append(helpers::order_by(expressions)),
schema: PhantomData,
state: PhantomData,
table: PhantomData,
}
}
}
impl<'a, S, T> SelectBuilder<'a, S, SelectOrderSet, T> {
pub fn limit(self, limit: usize) -> SelectBuilder<'a, S, SelectLimitSet, T> {
SelectBuilder {
sql: self.sql.append(helpers::limit(limit)),
schema: PhantomData,
state: PhantomData,
table: PhantomData,
}
}
}
impl<'a, S, T> SelectBuilder<'a, S, SelectLimitSet, T> {
pub fn offset(self, offset: usize) -> SelectBuilder<'a, S, SelectOffsetSet, T> {
SelectBuilder {
sql: self.sql.append(helpers::offset(offset)),
schema: PhantomData,
state: PhantomData,
table: PhantomData,
}
}
}
impl<'a, S, State, T> SelectBuilder<'a, S, State, T>
where
State: AsCteState,
T: SQLTable<'a, PostgresSchemaType, PostgresValue<'a>>,
{
#[inline]
pub fn into_cte(
self,
name: &'static str,
) -> super::CTEView<'a, <T as SQLTable<'a, PostgresSchemaType, PostgresValue<'a>>>::Aliased, Self>
{
super::CTEView::new(
<T as SQLTable<'a, PostgresSchemaType, PostgresValue<'a>>>::alias(name),
name,
self,
)
}
}
impl<'a, S, State, T> SelectBuilder<'a, S, State, T>
where
State: ExecutableState,
{
pub fn union(
self,
other: impl ToSQL<'a, PostgresValue<'a>>,
) -> SelectBuilder<'a, S, SelectSetOpSet, T> {
SelectBuilder {
sql: helpers::union(self.sql, other),
schema: PhantomData,
state: PhantomData,
table: PhantomData,
}
}
pub fn union_all(
self,
other: impl ToSQL<'a, PostgresValue<'a>>,
) -> SelectBuilder<'a, S, SelectSetOpSet, T> {
SelectBuilder {
sql: helpers::union_all(self.sql, other),
schema: PhantomData,
state: PhantomData,
table: PhantomData,
}
}
pub fn intersect(
self,
other: impl ToSQL<'a, PostgresValue<'a>>,
) -> SelectBuilder<'a, S, SelectSetOpSet, T> {
SelectBuilder {
sql: helpers::intersect(self.sql, other),
schema: PhantomData,
state: PhantomData,
table: PhantomData,
}
}
pub fn intersect_all(
self,
other: impl ToSQL<'a, PostgresValue<'a>>,
) -> SelectBuilder<'a, S, SelectSetOpSet, T> {
SelectBuilder {
sql: helpers::intersect_all(self.sql, other),
schema: PhantomData,
state: PhantomData,
table: PhantomData,
}
}
pub fn except(
self,
other: impl ToSQL<'a, PostgresValue<'a>>,
) -> SelectBuilder<'a, S, SelectSetOpSet, T> {
SelectBuilder {
sql: helpers::except(self.sql, other),
schema: PhantomData,
state: PhantomData,
table: PhantomData,
}
}
pub fn except_all(
self,
other: impl ToSQL<'a, PostgresValue<'a>>,
) -> SelectBuilder<'a, S, SelectSetOpSet, T> {
SelectBuilder {
sql: helpers::except_all(self.sql, other),
schema: PhantomData,
state: PhantomData,
table: PhantomData,
}
}
}
impl<'a, S, T> SelectBuilder<'a, S, SelectSetOpSet, T> {
pub fn order_by<TOrderBy>(
self,
expressions: TOrderBy,
) -> SelectBuilder<'a, S, SelectOrderSet, T>
where
TOrderBy: ToSQL<'a, PostgresValue<'a>>,
{
SelectBuilder {
sql: self.sql.append(helpers::order_by(expressions)),
schema: PhantomData,
state: PhantomData,
table: PhantomData,
}
}
pub fn limit(self, limit: usize) -> SelectBuilder<'a, S, SelectLimitSet, T> {
SelectBuilder {
sql: self.sql.append(helpers::limit(limit)),
schema: PhantomData,
state: PhantomData,
table: PhantomData,
}
}
pub fn offset(self, offset: usize) -> SelectBuilder<'a, S, SelectOffsetSet, T> {
SelectBuilder {
sql: self.sql.append(helpers::offset(offset)),
schema: PhantomData,
state: PhantomData,
table: 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> SelectBuilder<'a, S, State, T>
where
State: ForLockableState,
{
pub fn for_update(self) -> SelectBuilder<'a, S, SelectForSet, T> {
SelectBuilder {
sql: self.sql.append(helpers::for_update()),
schema: PhantomData,
state: PhantomData,
table: PhantomData,
}
}
pub fn for_share(self) -> SelectBuilder<'a, S, SelectForSet, T> {
SelectBuilder {
sql: self.sql.append(helpers::for_share()),
schema: PhantomData,
state: PhantomData,
table: PhantomData,
}
}
pub fn for_no_key_update(self) -> SelectBuilder<'a, S, SelectForSet, T> {
SelectBuilder {
sql: self.sql.append(helpers::for_no_key_update()),
schema: PhantomData,
state: PhantomData,
table: PhantomData,
}
}
pub fn for_key_share(self) -> SelectBuilder<'a, S, SelectForSet, T> {
SelectBuilder {
sql: self.sql.append(helpers::for_key_share()),
schema: PhantomData,
state: PhantomData,
table: PhantomData,
}
}
pub fn for_update_of<U: PostgresTable<'a>>(
self,
table: U,
) -> SelectBuilder<'a, S, SelectForSet, T> {
SelectBuilder {
sql: self.sql.append(helpers::for_update_of(table.name())),
schema: PhantomData,
state: PhantomData,
table: PhantomData,
}
}
pub fn for_share_of<U: PostgresTable<'a>>(
self,
table: U,
) -> SelectBuilder<'a, S, SelectForSet, T> {
SelectBuilder {
sql: self.sql.append(helpers::for_share_of(table.name())),
schema: PhantomData,
state: PhantomData,
table: PhantomData,
}
}
}
impl<'a, S, T> SelectBuilder<'a, S, SelectForSet, T> {
pub fn nowait(self) -> SelectBuilder<'a, S, SelectForSet, T> {
SelectBuilder {
sql: self.sql.append(helpers::nowait()),
schema: PhantomData,
state: PhantomData,
table: PhantomData,
}
}
pub fn skip_locked(self) -> SelectBuilder<'a, S, SelectForSet, T> {
SelectBuilder {
sql: self.sql.append(helpers::skip_locked()),
schema: PhantomData,
state: PhantomData,
table: 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,
};
assert_eq!(builder.to_sql().sql(), "SELECT *");
}
}