use crate::{
ColumnRef, DynIden, IntoIden, QueryStatementBuilder, QueryStatementWriter, SelectExpr,
SelectStatement, SimpleExpr, SqlWriter, SubQueryStatement, TableRef, Values,
{Alias, QueryBuilder},
};
use inherent::inherent;
#[derive(Debug, Clone, Default, PartialEq)]
pub struct CommonTableExpression {
pub(crate) table_name: Option<DynIden>,
pub(crate) cols: Vec<DynIden>,
pub(crate) query: Option<Box<SubQueryStatement>>,
pub(crate) materialized: Option<bool>,
}
impl CommonTableExpression {
pub fn new() -> CommonTableExpression {
Self::default()
}
pub fn table_name<T>(&mut self, table_name: T) -> &mut Self
where
T: IntoIden,
{
self.table_name = Some(table_name.into_iden());
self
}
pub fn column<C>(&mut self, col: C) -> &mut Self
where
C: IntoIden,
{
self.cols.push(col.into_iden());
self
}
pub fn columns<T, I>(&mut self, cols: I) -> &mut Self
where
T: IntoIden,
I: IntoIterator<Item = T>,
{
self.cols
.extend(cols.into_iter().map(|col| col.into_iden()));
self
}
pub fn materialized(&mut self, materialized: bool) -> &mut Self {
self.materialized = Some(materialized);
self
}
pub fn query<Q>(&mut self, query: Q) -> &mut Self
where
Q: QueryStatementBuilder,
{
self.query = Some(Box::new(query.into_sub_query_statement()));
self
}
pub fn from_select(select: SelectStatement) -> Self {
let mut cte = Self::default();
cte.try_set_cols_from_selects(&select.selects);
if let Some(from) = select.from.first() {
match from {
TableRef::Table(iden) => cte.set_table_name_from_select(iden),
TableRef::SchemaTable(_, iden) => cte.set_table_name_from_select(iden),
TableRef::DatabaseSchemaTable(_, _, iden) => cte.set_table_name_from_select(iden),
TableRef::TableAlias(_, iden) => cte.set_table_name_from_select(iden),
TableRef::SchemaTableAlias(_, _, iden) => cte.set_table_name_from_select(iden),
TableRef::DatabaseSchemaTableAlias(_, _, _, iden) => {
cte.set_table_name_from_select(iden)
}
_ => {}
}
}
cte.query = Some(Box::new(select.into_sub_query_statement()));
cte
}
fn set_table_name_from_select(&mut self, iden: &DynIden) {
self.table_name = Some(Alias::new(format!("cte_{}", iden.to_string())).into_iden())
}
pub fn try_set_cols_from_select(&mut self, select: &SelectStatement) -> bool {
self.try_set_cols_from_selects(&select.selects)
}
fn try_set_cols_from_selects(&mut self, selects: &[SelectExpr]) -> bool {
let vec: Option<Vec<DynIden>> = selects
.iter()
.map(|select| {
if let Some(ident) = &select.alias {
Some(ident.clone())
} else {
match &select.expr {
SimpleExpr::Column(column) => match column {
ColumnRef::Column(iden) => Some(iden.clone()),
ColumnRef::TableColumn(table, column) => Some(
Alias::new(format!("{}_{}", table.to_string(), column.to_string()))
.into_iden(),
),
ColumnRef::SchemaTableColumn(schema, table, column) => Some(
Alias::new(format!(
"{}_{}_{}",
schema.to_string(),
table.to_string(),
column.to_string()
))
.into_iden(),
),
_ => None,
},
_ => None,
}
}
})
.collect();
if let Some(c) = vec {
self.cols = c;
return true;
}
false
}
}
#[derive(Debug, Clone, PartialEq)]
pub enum SearchOrder {
BREADTH,
DEPTH,
}
#[derive(Debug, Clone, Default, PartialEq)]
pub struct Search {
pub(crate) order: Option<SearchOrder>,
pub(crate) expr: Option<SelectExpr>,
}
impl Search {
pub fn new_from_order_and_expr<EXPR>(order: SearchOrder, expr: EXPR) -> Self
where
EXPR: Into<SelectExpr>,
{
let expr = expr.into();
expr.alias.as_ref().unwrap();
Self {
order: Some(order),
expr: Some(expr),
}
}
pub fn new() -> Self {
Self::default()
}
pub fn order(&mut self, order: SearchOrder) -> &mut Self {
self.order = Some(order);
self
}
pub fn expr<EXPR>(&mut self, expr: EXPR) -> &mut Self
where
EXPR: Into<SelectExpr>,
{
let expr = expr.into();
expr.alias.as_ref().unwrap();
self.expr = Some(expr);
self
}
}
#[derive(Debug, Clone, Default, PartialEq)]
pub struct Cycle {
pub(crate) expr: Option<SimpleExpr>,
pub(crate) set_as: Option<DynIden>,
pub(crate) using: Option<DynIden>,
}
impl Cycle {
pub fn new_from_expr_set_using<EXPR, ID1, ID2>(expr: EXPR, set: ID1, using: ID2) -> Self
where
EXPR: Into<SimpleExpr>,
ID1: IntoIden,
ID2: IntoIden,
{
Self {
expr: Some(expr.into()),
set_as: Some(set.into_iden()),
using: Some(using.into_iden()),
}
}
pub fn new() -> Self {
Self::default()
}
pub fn expr<EXPR>(&mut self, expr: EXPR) -> &mut Self
where
EXPR: Into<SimpleExpr>,
{
self.expr = Some(expr.into());
self
}
pub fn set<ID>(&mut self, set: ID) -> &mut Self
where
ID: IntoIden,
{
self.set_as = Some(set.into_iden());
self
}
pub fn using<ID>(&mut self, using: ID) -> &mut Self
where
ID: IntoIden,
{
self.using = Some(using.into_iden());
self
}
}
#[derive(Debug, Clone, Default, PartialEq)]
pub struct WithClause {
pub(crate) recursive: bool,
pub(crate) search: Option<Search>,
pub(crate) cycle: Option<Cycle>,
pub(crate) cte_expressions: Vec<CommonTableExpression>,
}
impl WithClause {
pub fn new() -> Self {
Self::default()
}
pub fn recursive(&mut self, recursive: bool) -> &mut Self {
self.recursive = recursive;
self
}
pub fn search(&mut self, search: Search) -> &mut Self {
self.search = Some(search);
self
}
pub fn cycle(&mut self, cycle: Cycle) -> &mut Self {
self.cycle = Some(cycle);
self
}
pub fn cte(&mut self, cte: CommonTableExpression) -> &mut Self {
self.cte_expressions.push(cte);
self
}
pub fn query<T>(self, query: T) -> WithQuery
where
T: QueryStatementBuilder + 'static,
{
WithQuery::new().with_clause(self).query(query).to_owned()
}
}
impl From<CommonTableExpression> for WithClause {
fn from(cte: CommonTableExpression) -> WithClause {
WithClause::new().cte(cte).to_owned()
}
}
#[derive(Debug, Clone, Default, PartialEq)]
pub struct WithQuery {
pub(crate) with_clause: WithClause,
pub(crate) query: Option<Box<SubQueryStatement>>,
}
impl WithQuery {
pub fn new() -> Self {
Self::default()
}
pub fn with_clause(&mut self, with_clause: WithClause) -> &mut Self {
self.with_clause = with_clause;
self
}
pub fn recursive(&mut self, recursive: bool) -> &mut Self {
self.with_clause.recursive = recursive;
self
}
pub fn search(&mut self, search: Search) -> &mut Self {
self.with_clause.search = Some(search);
self
}
pub fn cycle(&mut self, cycle: Cycle) -> &mut Self {
self.with_clause.cycle = Some(cycle);
self
}
pub fn cte(&mut self, cte: CommonTableExpression) -> &mut Self {
self.with_clause.cte_expressions.push(cte);
self
}
pub fn query<T>(&mut self, query: T) -> &mut Self
where
T: QueryStatementBuilder,
{
self.query = Some(Box::new(query.into_sub_query_statement()));
self
}
}
impl QueryStatementBuilder for WithQuery {
fn build_collect_any_into(&self, query_builder: &dyn QueryBuilder, sql: &mut dyn SqlWriter) {
query_builder.prepare_with_query(self, sql);
}
fn into_sub_query_statement(self) -> SubQueryStatement {
SubQueryStatement::WithStatement(self)
}
}
#[inherent]
impl QueryStatementWriter for WithQuery {
pub fn build_collect_into<T: QueryBuilder>(&self, query_builder: T, sql: &mut dyn SqlWriter) {
query_builder.prepare_with_query(self, sql);
}
pub fn build_collect<T: QueryBuilder>(
&self,
query_builder: T,
sql: &mut dyn SqlWriter,
) -> String;
pub fn build<T: QueryBuilder>(&self, query_builder: T) -> (String, Values);
pub fn to_string<T: QueryBuilder>(&self, query_builder: T) -> String;
}