use std::marker::PhantomData;
use crate::{
Query,
builder::{delete::Delete, update::UpdateFrom},
query::{LowerProject, Table, WithSelect, select},
};
#[derive(Debug, Clone)]
pub struct Cte {
pub(crate) name: &'static str,
pub(crate) columns: Vec<&'static str>,
pub(crate) query: Query,
pub(crate) recursive: bool,
}
#[derive(Debug, Clone)]
pub struct CteDefinition<T> {
name: &'static str,
columns: &'static [&'static str],
query: Query,
marker: PhantomData<T>,
}
pub trait IntoCtes {
#[doc(hidden)]
fn into_ctes(self, recursive: bool) -> Vec<Cte>;
}
#[derive(Debug, Clone)]
pub struct WithClause {
ctes: Vec<Cte>,
}
impl<T> CteDefinition<T> {
pub fn new<Q>(name: &'static str, columns: &'static [&'static str], query: Q) -> Self
where
Q: Into<Query>,
{
Self {
name,
columns,
query: query.into(),
marker: PhantomData,
}
}
fn into_cte(self, recursive: bool) -> Cte {
Cte {
name: self.name,
columns: self.columns.to_vec(),
query: self.query,
recursive,
}
}
}
impl<T> IntoCtes for CteDefinition<T> {
fn into_ctes(self, recursive: bool) -> Vec<Cte> {
vec![self.into_cte(recursive)]
}
}
impl IntoCtes for Cte {
fn into_ctes(mut self, recursive: bool) -> Vec<Cte> {
self.recursive |= recursive;
vec![self]
}
}
impl IntoCtes for Vec<Cte> {
fn into_ctes(mut self, recursive: bool) -> Vec<Cte> {
if recursive {
for cte in &mut self {
cte.recursive = true;
}
}
self
}
}
impl<T> IntoCtes for Vec<CteDefinition<T>> {
fn into_ctes(self, recursive: bool) -> Vec<Cte> {
self.into_iter()
.map(|cte| cte.into_cte(recursive))
.collect()
}
}
macro_rules! impl_into_ctes_tuple {
($($T:ident),+) => {
impl<$($T,)+> IntoCtes for ($($T,)+)
where
$($T: IntoCtes,)+
{
fn into_ctes(self, recursive: bool) -> Vec<Cte> {
#[allow(non_snake_case)]
let ($($T,)+) = self;
let mut out = Vec::new();
$(
out.extend($T.into_ctes(recursive));
)+
out
}
}
};
}
crate::impl_for_all_tuples!(impl_into_ctes_tuple);
pub fn with<C>(ctes: C) -> WithClause
where
C: IntoCtes,
{
WithClause {
ctes: ctes.into_ctes(false),
}
}
pub fn with_recursive<C>(ctes: C) -> WithClause
where
C: IntoCtes,
{
WithClause {
ctes: ctes.into_ctes(true),
}
}
impl WithClause {
pub fn select<P>(self, project: P) -> WithSelect<P>
where
P: LowerProject,
{
let mut ctes = self.ctes.into_iter();
let first = ctes.next().expect("with() requires at least one CTE");
let mut stmt = select(project).with(first);
for cte in ctes {
stmt = stmt.with(cte);
}
stmt
}
pub fn update<M, T>(self, table: T) -> UpdateFrom<M>
where
T: crate::builder::update::UpdateTable<M>,
{
let mut stmt = crate::builder::update::update(table);
for cte in self.ctes {
stmt = stmt.with(cte);
}
stmt
}
pub fn delete<T>(self, table: Table<T>) -> Delete<T> {
let mut stmt = crate::builder::delete::delete_from(table);
for cte in self.ctes {
stmt = stmt.with(cte);
}
stmt
}
}