use crate::query::Select;
use crate::query::write_select;
use crate::types::Iden;
use crate::types::IntoIden;
use crate::types::write_iden;
use crate::value::Value;
use crate::value::write_value;
#[derive(Debug, Clone, Default, PartialEq)]
pub struct With {
ctes: Vec<CommonTableExpression>,
}
impl With {
pub fn new() -> Self {
Self::default()
}
pub fn cte(mut self, cte: CommonTableExpression) -> Self {
self.ctes.push(cte);
self
}
}
#[derive(Debug, Clone, PartialEq)]
pub struct CommonTableExpression {
name: Iden,
columns: Vec<Iden>,
query: Query,
materialized: Option<bool>,
}
impl CommonTableExpression {
pub fn new<T>(name: T) -> Self
where
T: IntoIden,
{
Self {
name: name.into_iden(),
columns: Vec::new(),
query: Query::Values(vec![]),
materialized: None,
}
}
pub fn values(mut self, values: Vec<Vec<Value>>) -> Self {
self.query = Query::Values(values);
self
}
pub fn select(mut self, select: Select) -> Self {
self.query = Query::Select(Box::new(select));
self
}
pub fn column<C>(mut self, col: C) -> Self
where
C: IntoIden,
{
self.columns.push(col.into_iden());
self
}
pub fn columns<T, I>(mut self, cols: I) -> Self
where
T: IntoIden,
I: IntoIterator<Item = T>,
{
self.columns
.extend(cols.into_iter().map(|col| col.into_iden()));
self
}
pub fn materialized(mut self, materialized: bool) -> Self {
self.materialized = Some(materialized);
self
}
}
#[derive(Debug, Clone, PartialEq)]
enum Query {
Select(Box<Select>),
Values(Vec<Vec<Value>>),
}
pub(crate) fn write_with<W: crate::writer::SqlWriter>(w: &mut W, with: &With) {
w.push_str("WITH ");
for (i, cte) in with.ctes.iter().enumerate() {
if i > 0 {
w.push_str(", ");
}
write_iden(w, &cte.name);
if cte.columns.is_empty() {
w.push_str(" ");
} else {
w.push_str(" (");
for (i, col) in cte.columns.iter().enumerate() {
if i > 0 {
w.push_str(", ");
}
write_iden(w, col);
}
w.push_str(") ");
}
w.push_str("AS ");
if let Some(materialized) = cte.materialized {
if materialized {
w.push_str("MATERIALIZED ");
} else {
w.push_str("NOT MATERIALIZED ");
}
}
match &cte.query {
Query::Select(select) => {
w.push_char('(');
write_select(w, select);
w.push_char(')');
}
Query::Values(values) => {
w.push_str("VALUES ");
for (j, row) in values.iter().enumerate() {
if j > 0 {
w.push_str(", ");
}
w.push_char('(');
for (k, val) in row.iter().enumerate() {
if k > 0 {
w.push_str(", ");
}
write_value(w, val);
}
w.push_char(')');
}
}
}
}
}