use std::borrow::Cow;
use std::marker::PhantomData;
use keelson_core::clause::{
ConflictClause, ConflictTarget, Cte, HasGroupBy, HasHaving, HasJoins, HasLimit, HasOffset,
HasOrderBy, HasReturning, HasSelectList, HasSet, HasTableRef, HasValues, HasWhere, HasWindows,
HasWith, IndexedBy, Join, JoinKind, NamedWindow, NullsPosition, OrderDef, OrderDirection,
TableRef, Values, Window,
};
use keelson_core::expr::{Expr, IntoExpr, IntoExprList, IntoIdent};
use keelson_core::{Mod, mod_fn};
use crate::extras::{Compound, CompoundOp, HasCompounds, HasOr, HasUpserts, Or};
use crate::statement::{HasExtraTables, HasTargetTable};
#[derive(Debug, Clone)]
pub struct CteChain {
cte: Cte,
}
pub fn with(name: impl Into<Cow<'static, str>>, body: impl IntoExpr) -> CteChain {
CteChain {
cte: Cte::new(name, body),
}
}
impl CteChain {
#[must_use]
pub fn columns(
mut self,
columns: impl IntoIterator<Item = impl Into<Cow<'static, str>>>,
) -> CteChain {
self.cte.columns = columns.into_iter().map(Into::into).collect();
self
}
#[must_use]
pub fn materialized(mut self) -> CteChain {
self.cte.materialized = Some(true);
self
}
#[must_use]
pub fn not_materialized(mut self) -> CteChain {
self.cte.materialized = Some(false);
self
}
}
impl<Q: HasWith> Mod<Q> for CteChain {
fn apply(self, q: &mut Q) {
q.with_mut().append_cte(self.cte);
}
}
pub fn recursive<Q: HasWith>(recursive: bool) -> impl Mod<Q> {
mod_fn(move |q: &mut Q| q.with_mut().set_recursive(recursive))
}
fn or_algorithm<Q: HasOr>(or: Or) -> impl Mod<Q> {
mod_fn(move |q: &mut Q| *q.or_mut() = Some(or))
}
pub fn or_rollback<Q: HasOr>() -> impl Mod<Q> {
or_algorithm(Or::Rollback)
}
pub fn or_abort<Q: HasOr>() -> impl Mod<Q> {
or_algorithm(Or::Abort)
}
pub fn or_replace<Q: HasOr>() -> impl Mod<Q> {
or_algorithm(Or::Replace)
}
pub fn or_fail<Q: HasOr>() -> impl Mod<Q> {
or_algorithm(Or::Fail)
}
pub fn or_ignore<Q: HasOr>() -> impl Mod<Q> {
or_algorithm(Or::Ignore)
}
pub fn columns<Q: HasSelectList>(columns: impl IntoExprList) -> impl Mod<Q> {
let columns = columns.into_expr_list();
mod_fn(move |q: &mut Q| q.select_list_mut().append_select(columns))
}
pub fn preload_columns<Q: HasSelectList>(columns: impl IntoExprList) -> impl Mod<Q> {
let columns = columns.into_expr_list();
mod_fn(move |q: &mut Q| q.select_list_mut().append_preload_select(columns))
}
pub trait TableSlot<Q> {
fn place(q: &mut Q, table: TableRef);
}
#[derive(Debug, Clone, Copy, Default)]
pub struct FromSlot;
#[derive(Debug, Clone, Copy, Default)]
pub struct TargetSlot;
#[derive(Debug, Clone, Copy, Default)]
pub struct ExtraSlot;
impl<Q: HasTableRef> TableSlot<Q> for FromSlot {
fn place(q: &mut Q, mut table: TableRef) {
table.joins.append(&mut q.table_ref_mut().joins);
*q.table_ref_mut() = table;
}
}
impl<Q: HasTargetTable> TableSlot<Q> for TargetSlot {
fn place(q: &mut Q, table: TableRef) {
*q.target_table_mut() = table;
}
}
impl<Q: HasExtraTables> TableSlot<Q> for ExtraSlot {
fn place(q: &mut Q, table: TableRef) {
q.extra_tables_mut().push(table);
}
}
#[derive(Debug, Clone)]
pub struct TableChain<S> {
table: TableRef,
slot: PhantomData<S>,
}
fn table_chain<S>(table: impl IntoExpr) -> TableChain<S> {
TableChain {
table: TableRef::new(table),
slot: PhantomData,
}
}
pub fn from_item(table: impl IntoExpr) -> TableChain<FromSlot> {
table_chain(table)
}
pub fn extra_from_item(table: impl IntoExpr) -> TableChain<ExtraSlot> {
table_chain(table)
}
pub fn target_table(table: impl IntoExpr) -> TableChain<TargetSlot> {
table_chain(table)
}
impl<S> TableChain<S> {
#[must_use]
pub fn as_(mut self, alias: impl Into<Cow<'static, str>>) -> TableChain<S> {
self.table.set_alias(alias);
self
}
#[must_use]
pub fn indexed_by(mut self, name: impl Into<Cow<'static, str>>) -> TableChain<S> {
self.table.indexed_by = Some(IndexedBy::Index(name.into()));
self
}
#[must_use]
pub fn not_indexed(mut self) -> TableChain<S> {
self.table.indexed_by = Some(IndexedBy::NotIndexed);
self
}
}
impl<Q, S: TableSlot<Q>> Mod<Q> for TableChain<S> {
fn apply(self, q: &mut Q) {
S::place(q, self.table);
}
}
#[derive(Debug, Clone)]
pub struct IntoChain {
table: TableRef,
}
pub fn into_table(table: impl IntoExpr) -> IntoChain {
IntoChain {
table: TableRef::new(table),
}
}
impl IntoChain {
#[must_use]
pub fn as_(mut self, alias: impl Into<Cow<'static, str>>) -> IntoChain {
self.table.set_alias(alias);
self
}
#[must_use]
pub fn columns(
mut self,
columns: impl IntoIterator<Item = impl Into<Cow<'static, str>>>,
) -> IntoChain {
self.table.set_columns(columns);
self
}
}
impl<Q: HasTableRef> Mod<Q> for IntoChain {
fn apply(self, q: &mut Q) {
*q.table_ref_mut() = self.table;
}
}
#[derive(Debug, Clone)]
pub struct JoinChain {
join: Join,
}
fn join_chain(kind: JoinKind, to: impl IntoExpr) -> JoinChain {
JoinChain {
join: Join::new(kind, TableRef::new(to)),
}
}
pub fn inner_join(table: impl IntoExpr) -> JoinChain {
join_chain(JoinKind::Inner, table)
}
pub fn left_join(table: impl IntoExpr) -> JoinChain {
join_chain(JoinKind::Left, table)
}
pub fn right_join(table: impl IntoExpr) -> JoinChain {
join_chain(JoinKind::Right, table)
}
pub fn full_join(table: impl IntoExpr) -> JoinChain {
join_chain(JoinKind::Full, table)
}
pub fn cross_join(table: impl IntoExpr) -> JoinChain {
join_chain(JoinKind::Cross, table)
}
impl JoinChain {
#[must_use]
pub fn as_(mut self, alias: impl Into<Cow<'static, str>>) -> JoinChain {
self.join.to.set_alias(alias);
self
}
#[must_use]
pub fn indexed_by(mut self, name: impl Into<Cow<'static, str>>) -> JoinChain {
self.join.to.indexed_by = Some(IndexedBy::Index(name.into()));
self
}
#[must_use]
pub fn not_indexed(mut self) -> JoinChain {
self.join.to.indexed_by = Some(IndexedBy::NotIndexed);
self
}
#[must_use]
pub fn natural(mut self) -> JoinChain {
self.join.natural = true;
self
}
#[must_use]
pub fn on(mut self, condition: impl IntoExpr) -> JoinChain {
self.join.append_on(condition);
self
}
#[must_use]
pub fn on_eq(self, a: impl IntoExpr, b: impl IntoExpr) -> JoinChain {
self.on(Expr::binary(a, "=", b).grouped())
}
#[must_use]
pub fn using(
mut self,
columns: impl IntoIterator<Item = impl Into<Cow<'static, str>>>,
) -> JoinChain {
self.join.append_using(columns);
self
}
}
impl From<JoinChain> for Join {
fn from(chain: JoinChain) -> Join {
chain.join
}
}
impl<Q: HasJoins> Mod<Q> for JoinChain {
fn apply(self, q: &mut Q) {
q.joins_mut().push(self.into());
}
}
impl TableChain<ExtraSlot> {
#[must_use]
pub fn join(mut self, join: impl Into<Join>) -> TableChain<ExtraSlot> {
self.table.joins.push(join.into());
self
}
}
pub fn where_<Q: HasWhere>(condition: impl IntoExpr) -> impl Mod<Q> {
let condition = condition.into_expr();
mod_fn(move |q: &mut Q| q.where_mut().append_where(condition))
}
pub fn having<Q: HasHaving>(condition: impl IntoExpr) -> impl Mod<Q> {
let condition = condition.into_expr();
mod_fn(move |q: &mut Q| q.having_mut().append_having(condition))
}
pub fn group_by<Q: HasGroupBy>(group: impl IntoExpr) -> impl Mod<Q> {
let group = group.into_expr();
mod_fn(move |q: &mut Q| q.group_by_mut().append_group(group))
}
pub fn window<Q: HasWindows>(
name: impl Into<Cow<'static, str>>,
definition: impl Mod<Window>,
) -> impl Mod<Q> {
let mut w = Window::default();
definition.apply(&mut w);
let named = NamedWindow::new(name, w);
mod_fn(move |q: &mut Q| q.windows_mut().append_window(named))
}
#[derive(Debug, Clone)]
pub struct OrderChain {
def: OrderDef,
}
pub fn order_by(expression: impl IntoExpr) -> OrderChain {
OrderChain {
def: OrderDef::new(expression),
}
}
impl OrderChain {
#[must_use]
pub fn asc(mut self) -> OrderChain {
self.def.direction = Some(OrderDirection::Asc);
self
}
#[must_use]
pub fn desc(mut self) -> OrderChain {
self.def.direction = Some(OrderDirection::Desc);
self
}
#[must_use]
pub fn nulls_first(mut self) -> OrderChain {
self.def.nulls = Some(NullsPosition::First);
self
}
#[must_use]
pub fn nulls_last(mut self) -> OrderChain {
self.def.nulls = Some(NullsPosition::Last);
self
}
#[must_use]
pub fn collate(mut self, name: impl Into<Cow<'static, str>>) -> OrderChain {
self.def.collation = Some(name.into());
self
}
}
impl<Q: HasOrderBy> Mod<Q> for OrderChain {
fn apply(self, q: &mut Q) {
q.order_by_mut().append_order(Expr::custom(self.def));
}
}
pub fn limit<Q: HasLimit>(count: impl IntoExpr) -> impl Mod<Q> {
let count = count.into_expr();
mod_fn(move |q: &mut Q| q.limit_mut().set_limit(count))
}
pub fn offset<Q: HasOffset>(start: impl IntoExpr) -> impl Mod<Q> {
let start = start.into_expr();
mod_fn(move |q: &mut Q| q.offset_mut().set_offset(start))
}
fn compound<Q: HasCompounds>(op: CompoundOp, query: impl IntoExpr) -> impl Mod<Q> {
let c = Compound::new(op, query);
mod_fn(move |q: &mut Q| q.compounds_mut().append_compound(c))
}
pub fn union<Q: HasCompounds>(query: impl IntoExpr) -> impl Mod<Q> {
compound(CompoundOp::Union, query)
}
pub fn union_all<Q: HasCompounds>(query: impl IntoExpr) -> impl Mod<Q> {
compound(CompoundOp::UnionAll, query)
}
pub fn intersect<Q: HasCompounds>(query: impl IntoExpr) -> impl Mod<Q> {
compound(CompoundOp::Intersect, query)
}
pub fn except<Q: HasCompounds>(query: impl IntoExpr) -> impl Mod<Q> {
compound(CompoundOp::Except, query)
}
pub fn returning<Q: HasReturning>(expressions: impl IntoExprList) -> impl Mod<Q> {
let expressions = expressions.into_expr_list();
mod_fn(move |q: &mut Q| q.returning_mut().append_returnings(expressions))
}
pub fn set<Q: HasSet>(assignment: impl IntoExpr) -> impl Mod<Q> {
let assignment = assignment.into_expr();
mod_fn(move |q: &mut Q| q.set_mut().append_set(assignment))
}
#[derive(Debug, Clone)]
pub struct SetChain {
column: Expr,
}
pub fn set_col(column: impl IntoIdent) -> SetChain {
SetChain {
column: Expr::ident(column),
}
}
impl SetChain {
pub fn to<Q: HasSet>(self, value: impl IntoExpr) -> impl Mod<Q> {
set(Expr::binary(self.column, "=", value))
}
pub fn to_arg<Q: HasSet>(self, value: impl keelson_core::ToValue) -> impl Mod<Q> {
set(Expr::binary(self.column, "=", Expr::arg(value)))
}
}
pub fn set_excluded<Q: HasSet>(
columns: impl IntoIterator<Item = impl Into<Cow<'static, str>>>,
) -> impl Mod<Q> {
let assignments: Vec<Expr> = columns
.into_iter()
.map(Into::into)
.filter(|c: &Cow<'static, str>| !c.is_empty())
.map(|c| {
Expr::join_with(
"",
(
Expr::ident(c.clone()),
Expr::raw(" = excluded."),
Expr::ident(c),
),
)
})
.collect();
mod_fn(move |q: &mut Q| q.set_mut().append_sets(assignments))
}
pub fn values<Q: HasValues>(row: impl IntoExprList) -> impl Mod<Q> {
let row = row.into_expr_list();
mod_fn(move |q: &mut Q| q.values_mut().append_values(row))
}
pub fn rows<Q: HasValues, R: IntoExprList>(rows: impl IntoIterator<Item = R>) -> impl Mod<Q> {
let rows: Vec<Vec<Expr>> = rows.into_iter().map(IntoExprList::into_expr_list).collect();
mod_fn(move |q: &mut Q| {
let values = q.values_mut();
for row in rows {
values.append_values(row);
}
})
}
pub fn values_from_query<Q: HasValues>(query: impl IntoExpr) -> impl Mod<Q> {
let query = query.into_expr();
mod_fn(move |q: &mut Q| *q.values_mut() = Values::from_query(query))
}
#[derive(Debug, Clone)]
pub struct ConflictChain {
target: ConflictTarget,
}
pub fn on_conflict(columns: impl IntoExprList) -> ConflictChain {
ConflictChain {
target: ConflictTarget::on_columns(columns),
}
}
impl ConflictChain {
#[must_use]
pub fn where_(mut self, predicate: impl IntoExpr) -> ConflictChain {
self.target.where_mut().append_where(predicate);
self
}
pub fn do_nothing(self) -> ConflictMod {
let mut clause = ConflictClause::do_nothing();
clause.target = self.target;
ConflictMod { clause }
}
pub fn do_update(self, body: impl Mod<ConflictClause>) -> ConflictMod {
let mut clause = ConflictClause::do_update();
clause.target = self.target;
body.apply(&mut clause);
ConflictMod { clause }
}
}
#[derive(Debug, Clone)]
pub struct ConflictMod {
clause: ConflictClause,
}
impl<Q: HasUpserts> Mod<Q> for ConflictMod {
fn apply(self, q: &mut Q) {
q.upserts_mut().push(self.clause);
}
}