use crate::{
expr::{Condition, ConditionHolder, IntoCondition, SimpleExpr},
types::{
ColumnRef, DynIden, IntoColumnRef, IntoIden, IntoTableRef, JoinExpr, JoinType, Order,
OrderExpr, TableRef, WindowStatement,
},
value::{IntoValue, Value, Values},
};
use super::traits::{QueryBuilderTrait, QueryStatementBuilder, QueryStatementWriter};
#[derive(Debug, Clone, Default)]
pub struct SelectStatement {
pub(crate) ctes: Vec<CommonTableExpr>,
pub(crate) distinct: Option<SelectDistinct>,
pub(crate) selects: Vec<SelectExpr>,
pub(crate) from: Vec<TableRef>,
pub(crate) join: Vec<JoinExpr>,
pub(crate) r#where: ConditionHolder,
pub(crate) groups: Vec<SimpleExpr>,
pub(crate) having: ConditionHolder,
pub(crate) unions: Vec<(UnionType, SelectStatement)>,
pub(crate) orders: Vec<OrderExpr>,
pub(crate) limit: Option<Value>,
pub(crate) offset: Option<Value>,
pub(crate) lock: Option<LockClause>,
pub(crate) windows: Vec<(DynIden, WindowStatement)>,
}
#[derive(Debug, Clone)]
pub struct CommonTableExpr {
pub(crate) name: DynIden,
pub(crate) query: Box<SelectStatement>,
pub(crate) recursive: bool,
}
#[derive(Debug, Clone)]
#[non_exhaustive]
pub enum SelectDistinct {
All,
Distinct,
DistinctRow,
DistinctOn(Vec<ColumnRef>),
}
#[derive(Debug, Clone)]
pub struct SelectExpr {
pub expr: SimpleExpr,
pub alias: Option<DynIden>,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[non_exhaustive]
pub enum LockType {
Update,
NoKeyUpdate,
Share,
KeyShare,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[non_exhaustive]
pub enum LockBehavior {
Nowait,
SkipLocked,
}
#[allow(dead_code)]
#[derive(Debug, Clone)]
pub struct LockClause {
pub(crate) r#type: LockType,
pub(crate) tables: Vec<TableRef>,
pub(crate) behavior: Option<LockBehavior>,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[non_exhaustive]
pub enum UnionType {
Intersect,
Distinct,
Except,
All,
}
impl<T> From<T> for SelectExpr
where
T: Into<SimpleExpr>,
{
fn from(expr: T) -> Self {
SelectExpr {
expr: expr.into(),
alias: None,
}
}
}
impl SelectStatement {
pub fn new() -> Self {
Self::default()
}
pub fn take(&mut self) -> Self {
Self {
ctes: std::mem::take(&mut self.ctes),
distinct: self.distinct.take(),
selects: std::mem::take(&mut self.selects),
from: std::mem::take(&mut self.from),
join: std::mem::take(&mut self.join),
r#where: std::mem::replace(&mut self.r#where, ConditionHolder::new()),
groups: std::mem::take(&mut self.groups),
having: std::mem::replace(&mut self.having, ConditionHolder::new()),
unions: std::mem::take(&mut self.unions),
orders: std::mem::take(&mut self.orders),
limit: self.limit.take(),
offset: self.offset.take(),
lock: self.lock.take(),
windows: std::mem::take(&mut self.windows),
}
}
pub fn column<C>(&mut self, col: C) -> &mut Self
where
C: IntoColumnRef,
{
self.selects.push(SelectExpr {
expr: SimpleExpr::Column(col.into_column_ref()),
alias: None,
});
self
}
pub fn columns<I, C>(&mut self, cols: I) -> &mut Self
where
I: IntoIterator<Item = C>,
C: IntoColumnRef,
{
for col in cols {
self.column(col);
}
self
}
pub fn expr<E>(&mut self, expr: E) -> &mut Self
where
E: Into<SimpleExpr>,
{
self.selects.push(SelectExpr {
expr: expr.into(),
alias: None,
});
self
}
pub fn expr_as<E, A>(&mut self, expr: E, alias: A) -> &mut Self
where
E: Into<SimpleExpr>,
A: IntoIden,
{
self.selects.push(SelectExpr {
expr: expr.into(),
alias: Some(alias.into_iden()),
});
self
}
pub fn from<T>(&mut self, tbl: T) -> &mut Self
where
T: IntoTableRef,
{
self.from.push(tbl.into_table_ref());
self
}
pub fn from_as<T, A>(&mut self, tbl: T, alias: A) -> &mut Self
where
T: IntoIden,
A: IntoIden,
{
self.from
.push(TableRef::TableAlias(tbl.into_iden(), alias.into_iden()));
self
}
pub fn from_subquery(&mut self, query: SelectStatement, alias: impl IntoIden) -> &mut Self {
self.from
.push(TableRef::SubQuery(Box::new(query), alias.into_iden()));
self
}
pub fn clear_selects(&mut self) -> &mut Self {
self.selects.clear();
self
}
pub fn join<T, C>(&mut self, join: JoinType, tbl: T, condition: C) -> &mut Self
where
T: IntoTableRef,
C: IntoCondition,
{
self.join.push(JoinExpr {
join,
table: tbl.into_table_ref(),
on: Some(crate::types::JoinOn::Condition(condition.into_condition())),
});
self
}
pub fn left_join<T, C>(&mut self, tbl: T, condition: C) -> &mut Self
where
T: IntoTableRef,
C: IntoCondition,
{
self.join(JoinType::LeftJoin, tbl, condition)
}
pub fn right_join<T, C>(&mut self, tbl: T, condition: C) -> &mut Self
where
T: IntoTableRef,
C: IntoCondition,
{
self.join(JoinType::RightJoin, tbl, condition)
}
pub fn full_outer_join<T, C>(&mut self, tbl: T, condition: C) -> &mut Self
where
T: IntoTableRef,
C: IntoCondition,
{
self.join(JoinType::FullOuterJoin, tbl, condition)
}
pub fn inner_join<T, C>(&mut self, tbl: T, condition: C) -> &mut Self
where
T: IntoTableRef,
C: IntoCondition,
{
self.join(JoinType::InnerJoin, tbl, condition)
}
pub fn cross_join<T>(&mut self, tbl: T) -> &mut Self
where
T: IntoTableRef,
{
self.join.push(JoinExpr {
join: JoinType::CrossJoin,
table: tbl.into_table_ref(),
on: None,
});
self
}
pub fn and_where<C>(&mut self, condition: C) -> &mut Self
where
C: IntoCondition,
{
self.r#where.add_and(condition);
self
}
pub fn cond_where(&mut self, condition: Condition) -> &mut Self {
self.r#where.add_and(condition);
self
}
pub fn group_by<C>(&mut self, col: C) -> &mut Self
where
C: IntoColumnRef,
{
self.groups.push(SimpleExpr::Column(col.into_column_ref()));
self
}
pub fn group_by_col<C>(&mut self, col: C) -> &mut Self
where
C: IntoColumnRef,
{
self.group_by(col)
}
pub fn group_by_columns<I, C>(&mut self, cols: I) -> &mut Self
where
I: IntoIterator<Item = C>,
C: IntoColumnRef,
{
for col in cols {
self.group_by(col);
}
self
}
pub fn and_having<C>(&mut self, condition: C) -> &mut Self
where
C: IntoCondition,
{
self.having.add_and(condition);
self
}
pub fn cond_having(&mut self, condition: Condition) -> &mut Self {
self.having.add_and(condition);
self
}
pub fn order_by<C>(&mut self, col: C, order: Order) -> &mut Self
where
C: IntoColumnRef,
{
use crate::types::OrderExprKind;
self.orders.push(OrderExpr {
expr: OrderExprKind::Expr(Box::new(SimpleExpr::Column(col.into_column_ref()))),
order,
nulls: None,
});
self
}
pub fn order_by_expr<E>(&mut self, expr: E, order: Order) -> &mut Self
where
E: Into<SimpleExpr>,
{
use crate::types::OrderExprKind;
self.orders.push(OrderExpr {
expr: OrderExprKind::Expr(Box::new(expr.into())),
order,
nulls: None,
});
self
}
pub fn limit<V>(&mut self, limit: V) -> &mut Self
where
V: IntoValue,
{
self.limit = Some(limit.into_value());
self
}
pub fn offset<V>(&mut self, offset: V) -> &mut Self
where
V: IntoValue,
{
self.offset = Some(offset.into_value());
self
}
pub fn distinct(&mut self) -> &mut Self {
self.distinct = Some(SelectDistinct::Distinct);
self
}
pub fn distinct_on<I, C>(&mut self, cols: I) -> &mut Self
where
I: IntoIterator<Item = C>,
C: IntoColumnRef,
{
let cols: Vec<ColumnRef> = cols.into_iter().map(|c| c.into_column_ref()).collect();
self.distinct = Some(SelectDistinct::DistinctOn(cols));
self
}
pub fn union(&mut self, query: SelectStatement) -> &mut Self {
self.unions.push((UnionType::Distinct, query));
self
}
pub fn union_all(&mut self, query: SelectStatement) -> &mut Self {
self.unions.push((UnionType::All, query));
self
}
pub fn intersect(&mut self, query: SelectStatement) -> &mut Self {
self.unions.push((UnionType::Intersect, query));
self
}
pub fn except(&mut self, query: SelectStatement) -> &mut Self {
self.unions.push((UnionType::Except, query));
self
}
pub fn with_cte<N>(&mut self, name: N, query: SelectStatement) -> &mut Self
where
N: IntoIden,
{
self.ctes.push(CommonTableExpr {
name: name.into_iden(),
query: Box::new(query),
recursive: false,
});
self
}
pub fn with_recursive_cte<N>(&mut self, name: N, query: SelectStatement) -> &mut Self
where
N: IntoIden,
{
self.ctes.push(CommonTableExpr {
name: name.into_iden(),
query: Box::new(query),
recursive: true,
});
self
}
pub fn window_as<T>(&mut self, name: T, window: WindowStatement) -> &mut Self
where
T: IntoIden,
{
self.windows.push((name.into_iden(), window));
self
}
pub fn lock(&mut self, lock_type: LockType) -> &mut Self {
self.lock = Some(LockClause {
r#type: lock_type,
tables: Vec::new(),
behavior: None,
});
self
}
pub fn lock_exclusive(&mut self) -> &mut Self {
self.lock(LockType::Update)
}
pub fn lock_shared(&mut self) -> &mut Self {
self.lock(LockType::Share)
}
pub fn apply_if<T, F>(&mut self, val: Option<T>, func: F) -> &mut Self
where
F: FnOnce(&mut Self, T),
{
if let Some(val) = val {
func(self, val);
}
self
}
pub fn conditions<T, F>(&mut self, b: bool, if_true: T, if_false: F) -> &mut Self
where
T: FnOnce(&mut Self),
F: FnOnce(&mut Self),
{
if b {
if_true(self)
} else {
if_false(self)
}
self
}
}
impl QueryStatementBuilder for SelectStatement {
fn build_any(&self, query_builder: &dyn QueryBuilderTrait) -> (String, Values) {
use crate::backend::{
MySqlQueryBuilder, PostgresQueryBuilder, QueryBuilder, SqliteQueryBuilder,
};
use std::any::Any;
let any_builder = query_builder as &dyn Any;
if let Some(pg) = any_builder.downcast_ref::<PostgresQueryBuilder>() {
return pg.build_select(self);
}
if let Some(mysql) = any_builder.downcast_ref::<MySqlQueryBuilder>() {
return mysql.build_select(self);
}
if let Some(sqlite) = any_builder.downcast_ref::<SqliteQueryBuilder>() {
return sqlite.build_select(self);
}
panic!(
"Unsupported query builder type. Use PostgresQueryBuilder, MySqlQueryBuilder, or SqliteQueryBuilder."
);
}
}
impl QueryStatementWriter for SelectStatement {}