use crate::ColumnRef;
use crate::DynIden;
use crate::IntoIden;
use crate::QueryStatementBuilder;
use crate::QueryStatementWriter;
use crate::SelectExpr;
use crate::SelectStatement;
use crate::SimpleExpr;
use crate::SqlWriter;
use crate::SubQueryStatement;
use crate::TableRef;
use crate::{Alias, QueryBuilder};
use std::ops::Deref;
#[derive(Debug, Clone, Default)]
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.get(0) {
match from.deref() {
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)]
pub enum SearchOrder {
BREADTH,
DEPTH,
}
#[derive(Debug, Clone, Default)]
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)]
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)]
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()
}
}
#[derive(Debug, Clone, Default)]
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)
}
}
impl QueryStatementWriter for WithQuery {
fn build_collect_into<T: QueryBuilder>(&self, query_builder: T, sql: &mut dyn SqlWriter) {
query_builder.prepare_with_query(self, sql);
}
}