use std::{fmt::Write, marker::PhantomData};
use super::SqlStatement;
use crate::{
sql::{
Column, ColumnIsForeignKey, CombineSelectableTables, CombinedSelectableTables,
FieldNameCharsConsListItem, FieldsConsListItem, ParameterBinder, SelectableTables,
SelectedValues, SelectedValuesContainsFieldWithName, SqlBool, SqlExpression, SqlType,
Table, TableHasOneForeignKey, TableMarker,
},
util::{TypedBool, TypedFalse, TypedTrue, TypesEqual},
};
pub trait SelectStatement: SqlStatement {
type OutputFields: FieldsConsListItem;
type SelectFrom: SelectFrom;
type SelectedValues;
type HasSelectedValues: TypedBool;
type HasWhereClause: TypedBool;
type HasGroupByClause: TypedBool;
type HasOrderByClause: TypedBool;
fn write_selected_values<'s, 'a>(
&'s self,
f: &mut String,
parameter_binder: &mut ParameterBinder<'a>,
) -> std::fmt::Result
where
's: 'a;
fn write_where_clause<'s, 'a>(
&'s self,
f: &mut String,
parameter_binder: &mut ParameterBinder<'a>,
) -> std::fmt::Result
where
's: 'a;
fn write_group_by_clause<'s, 'a>(
&'s self,
f: &mut String,
parameter_binder: &mut ParameterBinder<'a>,
) -> std::fmt::Result
where
's: 'a;
fn write_order_by_clause<'s, 'a>(
&'s self,
f: &mut String,
parameter_binder: &mut ParameterBinder<'a>,
) -> std::fmt::Result
where
's: 'a;
fn write_sql_string<'s, 'a>(
&'s self,
f: &mut String,
parameter_binder: &mut ParameterBinder<'a>,
) -> std::fmt::Result
where
's: 'a,
{
write!(f, "SELECT ")?;
self.write_selected_values(f, parameter_binder)?;
write!(f, " FROM ")?;
<Self as SelectStatement>::SelectFrom::write_sql_from_string(f)?;
self.write_where_clause(f, parameter_binder)?;
self.write_group_by_clause(f, parameter_binder)?;
self.write_order_by_clause(f, parameter_binder)
}
}
macro_rules! impl_sql_statement_for_select_statement {
{} => {
type OutputFields = <Self as SelectStatement>::OutputFields;
fn write_sql_string<'s, 'a>(
&'s self,
f: &mut String,
parameter_binder: &mut ParameterBinder<'a>,
) -> std::fmt::Result
where
's: 'a,
{
<Self as SelectStatement>::write_sql_string(&self, f, parameter_binder)
}
};
}
pub struct EmptySelectStatement<S: SelectFrom>(PhantomData<S>);
impl<S: SelectFrom> EmptySelectStatement<S> {
pub fn new() -> Self {
Self(PhantomData)
}
}
impl<S: SelectFrom + 'static> SelectStatement for EmptySelectStatement<S> {
type HasGroupByClause = TypedFalse;
type HasOrderByClause = TypedFalse;
type HasSelectedValues = TypedFalse;
type HasWhereClause = TypedFalse;
type OutputFields = <S::LeftMostTable as Table>::Fields;
type SelectFrom = S;
type SelectedValues = ();
fn write_selected_values<'s, 'a>(
&'s self,
f: &mut String,
_parameter_binder: &mut ParameterBinder<'a>,
) -> std::fmt::Result
where
's: 'a,
{
write!(f, "*")
}
fn write_where_clause<'s, 'a>(
&'s self,
_f: &mut String,
_parameter_binder: &mut ParameterBinder<'a>,
) -> std::fmt::Result
where
's: 'a,
{
Ok(())
}
fn write_group_by_clause<'s, 'a>(
&'s self,
_f: &mut String,
_parameter_binder: &mut ParameterBinder<'a>,
) -> std::fmt::Result
where
's: 'a,
{
Ok(())
}
fn write_order_by_clause<'s, 'a>(
&'s self,
_f: &mut String,
_parameter_binder: &mut ParameterBinder<'a>,
) -> std::fmt::Result
where
's: 'a,
{
Ok(())
}
}
impl<S: SelectFrom + 'static> SqlStatement for EmptySelectStatement<S> {
impl_sql_statement_for_select_statement! {}
}
pub struct WithSelectedValues<
S: SelectFrom,
T: SelectStatement<HasSelectedValues = TypedFalse>,
V: SelectedValues<S::SelectableTables>,
> {
statement: T,
values: V,
phantom: PhantomData<S>,
}
impl<
S: SelectFrom + 'static,
T: SelectStatement<HasSelectedValues = TypedFalse>,
V: SelectedValues<S::SelectableTables> + 'static,
> SelectStatement for WithSelectedValues<S, T, V>
{
type HasGroupByClause = T::HasGroupByClause;
type HasOrderByClause = T::HasOrderByClause;
type HasSelectedValues = TypedTrue;
type HasWhereClause = T::HasWhereClause;
type OutputFields = V::Fields;
type SelectFrom = S;
type SelectedValues = V;
fn write_selected_values<'s, 'a>(
&'s self,
f: &mut String,
parameter_binder: &mut ParameterBinder<'a>,
) -> std::fmt::Result
where
's: 'a,
{
self.values.write_sql_string(f, parameter_binder)
}
fn write_where_clause<'s, 'a>(
&'s self,
f: &mut String,
parameter_binder: &mut ParameterBinder<'a>,
) -> std::fmt::Result
where
's: 'a,
{
self.statement.write_where_clause(f, parameter_binder)
}
fn write_group_by_clause<'s, 'a>(
&'s self,
f: &mut String,
parameter_binder: &mut ParameterBinder<'a>,
) -> std::fmt::Result
where
's: 'a,
{
self.statement.write_group_by_clause(f, parameter_binder)
}
fn write_order_by_clause<'s, 'a>(
&'s self,
f: &mut String,
parameter_binder: &mut ParameterBinder<'a>,
) -> std::fmt::Result
where
's: 'a,
{
self.statement.write_order_by_clause(f, parameter_binder)
}
}
impl<
S: SelectFrom + 'static,
T: SelectStatement<HasSelectedValues = TypedFalse>,
V: SelectedValues<S::SelectableTables> + 'static,
> SqlStatement for WithSelectedValues<S, T, V>
{
impl_sql_statement_for_select_statement! {}
}
pub trait SelectValues: SelectStatement<HasSelectedValues = TypedFalse> {
fn select<V: SelectedValues<<Self::SelectFrom as SelectFrom>::SelectableTables>>(
self,
values: V,
) -> WithSelectedValues<Self::SelectFrom, Self, V> {
WithSelectedValues {
statement: self,
values,
phantom: PhantomData,
}
}
}
impl<T: SelectStatement<HasSelectedValues = TypedFalse>> SelectValues for T {}
pub struct WithWhereClause<
S: SelectFrom,
T: SelectStatement<HasWhereClause = TypedFalse>,
C: SqlExpression<S::SelectableTables, SqlType = SqlBool>,
> {
statement: T,
condition: C,
phantom: PhantomData<S>,
}
impl<
S: SelectFrom + 'static,
T: SelectStatement<HasWhereClause = TypedFalse>,
C: SqlExpression<S::SelectableTables, SqlType = SqlBool> + 'static,
> SelectStatement for WithWhereClause<S, T, C>
{
type HasGroupByClause = T::HasGroupByClause;
type HasOrderByClause = T::HasOrderByClause;
type HasSelectedValues = T::HasSelectedValues;
type HasWhereClause = TypedTrue;
type OutputFields = <T as SelectStatement>::OutputFields;
type SelectFrom = S;
type SelectedValues = T::SelectedValues;
fn write_where_clause<'s, 'a>(
&'s self,
f: &mut String,
parameter_binder: &mut ParameterBinder<'a>,
) -> std::fmt::Result
where
's: 'a,
{
write!(f, " WHERE ")?;
self.condition.write_sql_string(f, parameter_binder)
}
fn write_selected_values<'s, 'a>(
&'s self,
f: &mut String,
parameter_binder: &mut ParameterBinder<'a>,
) -> std::fmt::Result
where
's: 'a,
{
self.statement.write_selected_values(f, parameter_binder)
}
fn write_group_by_clause<'s, 'a>(
&'s self,
f: &mut String,
parameter_binder: &mut ParameterBinder<'a>,
) -> std::fmt::Result
where
's: 'a,
{
self.statement.write_group_by_clause(f, parameter_binder)
}
fn write_order_by_clause<'s, 'a>(
&'s self,
f: &mut String,
parameter_binder: &mut ParameterBinder<'a>,
) -> std::fmt::Result
where
's: 'a,
{
self.statement.write_order_by_clause(f, parameter_binder)
}
}
impl<
S: SelectFrom + 'static,
T: SelectStatement<HasWhereClause = TypedFalse>,
C: SqlExpression<S::SelectableTables, SqlType = SqlBool> + 'static,
> SqlStatement for WithWhereClause<S, T, C>
{
impl_sql_statement_for_select_statement! {}
}
pub trait Filter: SelectStatement<HasWhereClause = TypedFalse> {
fn filter<
C: SqlExpression<<Self::SelectFrom as SelectFrom>::SelectableTables, SqlType = SqlBool>,
>(
self,
condition: C,
) -> WithWhereClause<Self::SelectFrom, Self, C> {
WithWhereClause {
statement: self,
condition,
phantom: PhantomData,
}
}
}
impl<T: SelectStatement<HasWhereClause = TypedFalse>> Filter for T {}
pub struct WithGroupByClause<
S: SelectFrom,
T: SelectStatement<HasGroupByClause = TypedFalse>,
G: SqlExpression<S::SelectableTables>,
> {
statement: T,
group_by: G,
phantom: PhantomData<S>,
}
impl<
S: SelectFrom + 'static,
T: SelectStatement<HasGroupByClause = TypedFalse>,
G: SqlExpression<S::SelectableTables> + 'static,
> SelectStatement for WithGroupByClause<S, T, G>
{
type HasGroupByClause = TypedTrue;
type HasOrderByClause = T::HasOrderByClause;
type HasSelectedValues = T::HasSelectedValues;
type HasWhereClause = T::HasWhereClause;
type OutputFields = <T as SelectStatement>::OutputFields;
type SelectFrom = S;
type SelectedValues = T::SelectedValues;
fn write_group_by_clause<'s, 'a>(
&'s self,
f: &mut String,
parameter_binder: &mut ParameterBinder<'a>,
) -> std::fmt::Result
where
's: 'a,
{
write!(f, " GROUP BY ")?;
self.group_by.write_sql_string(f, parameter_binder)
}
fn write_selected_values<'s, 'a>(
&'s self,
f: &mut String,
parameter_binder: &mut ParameterBinder<'a>,
) -> std::fmt::Result
where
's: 'a,
{
self.statement.write_selected_values(f, parameter_binder)
}
fn write_where_clause<'s, 'a>(
&'s self,
f: &mut String,
parameter_binder: &mut ParameterBinder<'a>,
) -> std::fmt::Result
where
's: 'a,
{
self.statement.write_where_clause(f, parameter_binder)
}
fn write_order_by_clause<'s, 'a>(
&'s self,
f: &mut String,
parameter_binder: &mut ParameterBinder<'a>,
) -> std::fmt::Result
where
's: 'a,
{
self.statement.write_order_by_clause(f, parameter_binder)
}
}
impl<
S: SelectFrom + 'static,
T: SelectStatement<HasGroupByClause = TypedFalse>,
G: SqlExpression<S::SelectableTables> + 'static,
> SqlStatement for WithGroupByClause<S, T, G>
{
impl_sql_statement_for_select_statement! {}
}
pub trait GroupBy: SelectStatement<HasGroupByClause = TypedFalse> {
fn group_by<G: SqlExpression<<Self::SelectFrom as SelectFrom>::SelectableTables>>(
self,
group_by: G,
) -> WithGroupByClause<Self::SelectFrom, Self, G> {
WithGroupByClause {
statement: self,
group_by,
phantom: PhantomData,
}
}
}
impl<T: SelectStatement<HasGroupByClause = TypedFalse>> GroupBy for T {}
pub trait Ordering {
const ORDER_STR: &'static str;
}
pub struct AscendingOrder;
impl Ordering for AscendingOrder {
const ORDER_STR: &'static str = "";
}
pub struct DescendingOrder;
impl Ordering for DescendingOrder {
const ORDER_STR: &'static str = " DESC";
}
pub struct WithOrderByClause<
S: SelectFrom,
T: SelectStatement<HasOrderByClause = TypedFalse>,
B: SqlExpression<S::SelectableTables>,
O: Ordering,
> {
statement: T,
order_by: B,
_phantom: (PhantomData<S>, PhantomData<O>),
}
impl<
S: SelectFrom + 'static,
T: SelectStatement<HasOrderByClause = TypedFalse>,
B: SqlExpression<S::SelectableTables> + 'static,
O: Ordering + 'static,
> SelectStatement for WithOrderByClause<S, T, B, O>
{
type HasGroupByClause = T::HasGroupByClause;
type HasOrderByClause = TypedTrue;
type HasSelectedValues = T::HasSelectedValues;
type HasWhereClause = T::HasWhereClause;
type OutputFields = <T as SelectStatement>::OutputFields;
type SelectFrom = S;
type SelectedValues = T::SelectedValues;
fn write_group_by_clause<'s, 'a>(
&'s self,
f: &mut String,
parameter_binder: &mut ParameterBinder<'a>,
) -> std::fmt::Result
where
's: 'a,
{
self.statement.write_group_by_clause(f, parameter_binder)
}
fn write_selected_values<'s, 'a>(
&'s self,
f: &mut String,
parameter_binder: &mut ParameterBinder<'a>,
) -> std::fmt::Result
where
's: 'a,
{
self.statement.write_selected_values(f, parameter_binder)
}
fn write_where_clause<'s, 'a>(
&'s self,
f: &mut String,
parameter_binder: &mut ParameterBinder<'a>,
) -> std::fmt::Result
where
's: 'a,
{
self.statement.write_where_clause(f, parameter_binder)
}
fn write_order_by_clause<'s, 'a>(
&'s self,
f: &mut String,
parameter_binder: &mut ParameterBinder<'a>,
) -> std::fmt::Result
where
's: 'a,
{
write!(f, " ORDER BY ")?;
self.order_by.write_sql_string(f, parameter_binder)?;
write!(f, "{}", O::ORDER_STR)
}
}
impl<
S: SelectFrom + 'static,
T: SelectStatement<HasOrderByClause = TypedFalse>,
B: SqlExpression<S::SelectableTables> + 'static,
O: Ordering + 'static,
> SqlStatement for WithOrderByClause<S, T, B, O>
{
impl_sql_statement_for_select_statement! {}
}
pub trait OrderBy: SelectStatement<HasOrderByClause = TypedFalse> {
fn order_by_ascending<B: SqlExpression<<Self::SelectFrom as SelectFrom>::SelectableTables>>(
self,
order_by: B,
) -> WithOrderByClause<Self::SelectFrom, Self, B, AscendingOrder> {
WithOrderByClause {
statement: self,
order_by,
_phantom: (PhantomData, PhantomData),
}
}
fn order_by_descending<B: SqlExpression<<Self::SelectFrom as SelectFrom>::SelectableTables>>(
self,
order_by: B,
) -> WithOrderByClause<Self::SelectFrom, Self, B, DescendingOrder> {
WithOrderByClause {
statement: self,
order_by,
_phantom: (PhantomData, PhantomData),
}
}
}
impl<T: SelectStatement<HasOrderByClause = TypedFalse>> OrderBy for T {}
pub trait SelectedValueToOrderBy {
type Name: FieldNameCharsConsListItem;
const NAME_STR: &'static str;
}
pub struct WithOrderBySelectedValueClause<
S: SelectFrom,
T: SelectStatement<HasOrderByClause = TypedFalse>,
B: SelectedValueToOrderBy,
O: Ordering,
> {
statement: T,
_order_by: B,
_phantom: (PhantomData<S>, PhantomData<O>),
}
impl<
S: SelectFrom + 'static,
T: SelectStatement<HasOrderByClause = TypedFalse>,
B: SelectedValueToOrderBy + 'static,
O: Ordering + 'static,
> SelectStatement for WithOrderBySelectedValueClause<S, T, B, O>
{
type HasGroupByClause = T::HasGroupByClause;
type HasOrderByClause = TypedTrue;
type HasSelectedValues = T::HasSelectedValues;
type HasWhereClause = T::HasWhereClause;
type OutputFields = <T as SelectStatement>::OutputFields;
type SelectFrom = S;
type SelectedValues = T::SelectedValues;
fn write_group_by_clause<'s, 'a>(
&'s self,
f: &mut String,
parameter_binder: &mut ParameterBinder<'a>,
) -> std::fmt::Result
where
's: 'a,
{
self.statement.write_group_by_clause(f, parameter_binder)
}
fn write_selected_values<'s, 'a>(
&'s self,
f: &mut String,
parameter_binder: &mut ParameterBinder<'a>,
) -> std::fmt::Result
where
's: 'a,
{
self.statement.write_selected_values(f, parameter_binder)
}
fn write_where_clause<'s, 'a>(
&'s self,
f: &mut String,
parameter_binder: &mut ParameterBinder<'a>,
) -> std::fmt::Result
where
's: 'a,
{
self.statement.write_where_clause(f, parameter_binder)
}
fn write_order_by_clause<'s, 'a>(
&'s self,
f: &mut String,
_parameter_binder: &mut ParameterBinder<'a>,
) -> std::fmt::Result
where
's: 'a,
{
write!(f, " ORDER BY {}{}", B::NAME_STR, O::ORDER_STR)
}
}
impl<
S: SelectFrom + 'static,
T: SelectStatement<HasOrderByClause = TypedFalse>,
B: SelectedValueToOrderBy + 'static,
O: Ordering + 'static,
> SqlStatement for WithOrderBySelectedValueClause<S, T, B, O>
{
impl_sql_statement_for_select_statement! {}
}
pub trait OrderBySelectedValue<S: SelectableTables>:
SelectStatement<HasOrderByClause = TypedFalse>
where
Self::SelectedValues: SelectedValues<S>,
{
fn order_by_selected_value_ascending<B: SelectedValueToOrderBy>(
self,
order_by: B,
) -> WithOrderBySelectedValueClause<Self::SelectFrom, Self, B, AscendingOrder>
where
Self::SelectedValues: SelectedValuesContainsFieldWithName<B::Name>,
{
WithOrderBySelectedValueClause {
statement: self,
_order_by: order_by,
_phantom: (PhantomData, PhantomData),
}
}
fn order_by_selected_value_descending<B: SelectedValueToOrderBy>(
self,
order_by: B,
) -> WithOrderBySelectedValueClause<Self::SelectFrom, Self, B, DescendingOrder>
where
Self::SelectedValues: SelectedValuesContainsFieldWithName<B::Name>,
{
WithOrderBySelectedValueClause {
statement: self,
_order_by: order_by,
_phantom: (PhantomData, PhantomData),
}
}
}
impl<S: SelectableTables, T: SelectStatement<HasOrderByClause = TypedFalse>> OrderBySelectedValue<S>
for T
where
T::SelectedValues: SelectedValues<S>,
{
}
pub trait SelectFrom: Sized {
type SelectableTables: SelectableTables;
type LeftMostTable: Table;
fn write_sql_from_string(f: &mut String) -> std::fmt::Result;
fn write_sql_from_string_without_left(f: &mut String) -> std::fmt::Result;
fn find(self) -> EmptySelectStatement<Self> {
EmptySelectStatement::new()
}
}
impl<T: TableMarker> SelectFrom for T {
type LeftMostTable = T::Table;
type SelectableTables = T::Table;
fn write_sql_from_string(f: &mut String) -> std::fmt::Result {
write!(f, "\"{}\"", T::Table::TABLE_NAME)
}
fn write_sql_from_string_without_left(_f: &mut String) -> std::fmt::Result {
Ok(())
}
}
pub struct InnerJoined<
A: SelectFrom,
B: SelectFrom,
C: Column<Table = A::LeftMostTable> + ColumnIsForeignKey<B::LeftMostTable>,
>(PhantomData<A>, PhantomData<B>, PhantomData<C>)
where
A::SelectableTables: CombineSelectableTables<B::SelectableTables>,
(
<<C as Column>::SqlType as SqlType>::NonNullSqlType,
<<B::LeftMostTable as Table>::IdColumn as Column>::SqlType,
): TypesEqual;
impl<
A: SelectFrom,
B: SelectFrom,
C: Column<Table = A::LeftMostTable> + ColumnIsForeignKey<B::LeftMostTable>,
> InnerJoined<A, B, C>
where
A::SelectableTables: CombineSelectableTables<B::SelectableTables>,
(
<<C as Column>::SqlType as SqlType>::NonNullSqlType,
<<B::LeftMostTable as Table>::IdColumn as Column>::SqlType,
): TypesEqual,
{
pub fn new() -> Self {
Self(PhantomData, PhantomData, PhantomData)
}
}
impl<
A: SelectFrom,
B: SelectFrom,
C: Column<Table = A::LeftMostTable> + ColumnIsForeignKey<B::LeftMostTable>,
> SelectFrom for InnerJoined<A, B, C>
where
A::SelectableTables: CombineSelectableTables<B::SelectableTables>,
(
<<C as Column>::SqlType as SqlType>::NonNullSqlType,
<<B::LeftMostTable as Table>::IdColumn as Column>::SqlType,
): TypesEqual,
{
type LeftMostTable = A::LeftMostTable;
type SelectableTables = CombinedSelectableTables<A::SelectableTables, B::SelectableTables>;
fn write_sql_from_string(f: &mut String) -> std::fmt::Result {
A::write_sql_from_string(f)?;
Self::write_sql_from_string_without_left(f)
}
fn write_sql_from_string_without_left(f: &mut String) -> std::fmt::Result {
write!(
f,
" INNER JOIN \"{}\" ON \"{}\".\"{}\" = \"{}\".\"id\"",
B::LeftMostTable::TABLE_NAME,
A::LeftMostTable::TABLE_NAME,
<C as Column>::COLUMN_NAME,
B::LeftMostTable::TABLE_NAME
)?;
B::write_sql_from_string_without_left(f)
}
}
pub trait InnerJoinTrait: Sized + SelectFrom {
fn inner_join<S: SelectFrom>(self, _with: S) -> InnerJoined<Self, S, <Self::LeftMostTable as TableHasOneForeignKey<S::LeftMostTable>>::ForeignKeyColumn>
where
Self::LeftMostTable: TableHasOneForeignKey<S::LeftMostTable>,
<Self as SelectFrom>::SelectableTables: CombineSelectableTables<<S as SelectFrom>::SelectableTables>,
(<<<Self::LeftMostTable as TableHasOneForeignKey<S::LeftMostTable>>::ForeignKeyColumn as Column>::SqlType as SqlType>::NonNullSqlType, <<S::LeftMostTable as Table>::IdColumn as Column>::SqlType): TypesEqual
{
InnerJoined::new()
}
}
impl<S: SelectFrom> InnerJoinTrait for S {}
pub trait InnerJoinOnTrait: Sized + SelectFrom {
fn inner_join_on_column<
S: SelectFrom,
C: Column<Table = Self::LeftMostTable> + ColumnIsForeignKey<S::LeftMostTable>,
>(
self,
_column: C,
_with: S,
) -> InnerJoined<Self, S, C>
where
<Self as SelectFrom>::SelectableTables:
CombineSelectableTables<<S as SelectFrom>::SelectableTables>,
(
<<C as Column>::SqlType as SqlType>::NonNullSqlType,
<<S::LeftMostTable as Table>::IdColumn as Column>::SqlType,
): TypesEqual,
{
InnerJoined::new()
}
}
impl<S: SelectFrom> InnerJoinOnTrait for S {}