use std::marker::PhantomData;
use super::{Select, Selection, SortDir};
use crate::dialect::Dialect;
use crate::expr::Value;
use crate::render::{Fragment, QuerySink, Sink};
use crate::row::SameShape;
#[derive(Clone, Copy, PartialEq, Eq)]
enum SetOpKind {
Union,
UnionAll,
Intersect,
Except,
}
impl SetOpKind {
fn keyword(&self) -> &'static str {
match self {
SetOpKind::Union => " UNION ",
SetOpKind::UnionAll => " UNION ALL ",
SetOpKind::Intersect => " INTERSECT ",
SetOpKind::Except => " EXCEPT ",
}
}
}
pub struct SetOp<D, Output> {
first: Fragment,
rest: Vec<(SetOpKind, Fragment)>,
order_by: Vec<(u32, SortDir)>,
limit: Option<super::RowCount>,
offset: Option<super::RowCount>,
_marker: PhantomData<fn() -> (D, Output)>,
}
impl<D: Dialect, L> SetOp<D, crate::row::Row<L>> {
pub fn order_by_column<K, Idx>(self, _key: K, dir: SortDir) -> Self
where
K: crate::row::LookupKey,
L: crate::row::Field<K::Key, Idx>,
Idx: crate::scope::Position,
{
self.order_by_ordinal(<Idx as crate::scope::Position>::POSITION, dir)
}
}
impl<D: Dialect, V: crate::select::SingleColumn> SetOp<D, V> {
pub fn order_by(self, dir: SortDir) -> Self {
self.order_by_ordinal(1, dir)
}
}
impl<D: Dialect, Output> SetOp<D, Output> {
fn new(first: Fragment) -> Self {
SetOp {
first,
rest: Vec::new(),
order_by: Vec::new(),
limit: None,
offset: None,
_marker: PhantomData,
}
}
fn push(mut self, kind: SetOpKind, branch: Fragment) -> Self {
self.rest.push((kind, branch));
self
}
pub fn union<ScopeB, SelB, IdxB>(self, other: &Select<D, ScopeB, SelB>) -> Self
where
SelB: Selection<ScopeB, IdxB>,
SelB::Output: SameShape<Output>,
{
self.push(SetOpKind::Union, other.fragment::<IdxB>())
}
pub fn union_all<ScopeB, SelB, IdxB>(self, other: &Select<D, ScopeB, SelB>) -> Self
where
SelB: Selection<ScopeB, IdxB>,
SelB::Output: SameShape<Output>,
{
self.push(SetOpKind::UnionAll, other.fragment::<IdxB>())
}
pub fn intersect<ScopeB, SelB, IdxB>(self, other: &Select<D, ScopeB, SelB>) -> Self
where
SelB: Selection<ScopeB, IdxB>,
SelB::Output: SameShape<Output>,
{
self.push(SetOpKind::Intersect, other.fragment::<IdxB>())
}
pub fn except<ScopeB, SelB, IdxB>(self, other: &Select<D, ScopeB, SelB>) -> Self
where
SelB: Selection<ScopeB, IdxB>,
SelB::Output: SameShape<Output>,
{
self.push(SetOpKind::Except, other.fragment::<IdxB>())
}
fn order_by_ordinal(mut self, position: u32, dir: SortDir) -> Self {
self.order_by.push((position, dir));
self
}
pub fn limit(mut self, n: impl super::IntoRowCount) -> Self {
self.limit = Some(n.into_row_count());
self
}
pub fn offset(mut self, n: impl super::IntoRowCount) -> Self {
self.offset = Some(n.into_row_count());
self
}
pub fn count_sql(&self, _dialect: D) -> (String, Vec<Value>) {
let mut sink = QuerySink::<D>::new();
crate::render::render_count_wrapped::<D>(&mut sink, |sink| self.render_branches(sink));
sink.finish()
}
pub fn to_sql(&self, _dialect: D) -> (String, Vec<Value>) {
let mut sink = QuerySink::<D>::new();
self.render_branches(&mut sink);
self.render_ordering(&mut sink);
sink.finish()
}
fn render_branches(&self, sink: &mut QuerySink<D>) {
let branch = |sink: &mut QuerySink<D>, sql: &Fragment| {
if D::PARENTHESIZED_SET_OP_BRANCHES {
sink.ch('(');
sql.splice_into(sink);
sink.ch(')');
} else {
sink.text("SELECT * FROM (");
sql.splice_into(sink);
sink.ch(')');
}
};
let changes = self
.rest
.windows(2)
.filter(|pair| pair[0].0 != pair[1].0)
.count();
for _ in 0..changes {
if D::PARENTHESIZED_SET_OP_BRANCHES {
sink.ch('(');
} else {
sink.text("SELECT * FROM (");
}
}
branch(sink, &self.first);
for (i, (kind, part)) in self.rest.iter().enumerate() {
if i > 0 && self.rest[i - 1].0 != *kind {
sink.ch(')');
}
sink.text(kind.keyword());
branch(sink, part);
}
}
fn render_ordering(&self, sink: &mut QuerySink<D>) {
if !self.order_by.is_empty() {
sink.text(" ORDER BY ");
for (i, (position, dir)) in self.order_by.iter().enumerate() {
if i > 0 {
sink.text(", ");
}
sink.text(&position.to_string());
sink.text(crate::render::dir_keyword(*dir));
}
}
crate::select::render_limit_offset::<D>(sink, self.limit.as_ref(), self.offset.as_ref());
}
}
impl<D: Dialect, Scope, Sel> Select<D, Scope, Sel> {
pub fn union<ScopeB, SelB, IdxA, IdxB>(
&self,
other: &Select<D, ScopeB, SelB>,
) -> SetOp<D, Sel::Output>
where
Sel: Selection<Scope, IdxA>,
SelB: Selection<ScopeB, IdxB>,
SelB::Output: SameShape<Sel::Output>,
{
SetOp::new(self.fragment::<IdxA>()).union(other)
}
pub fn union_all<ScopeB, SelB, IdxA, IdxB>(
&self,
other: &Select<D, ScopeB, SelB>,
) -> SetOp<D, Sel::Output>
where
Sel: Selection<Scope, IdxA>,
SelB: Selection<ScopeB, IdxB>,
SelB::Output: SameShape<Sel::Output>,
{
SetOp::new(self.fragment::<IdxA>()).union_all(other)
}
pub fn intersect<ScopeB, SelB, IdxA, IdxB>(
&self,
other: &Select<D, ScopeB, SelB>,
) -> SetOp<D, Sel::Output>
where
Sel: Selection<Scope, IdxA>,
SelB: Selection<ScopeB, IdxB>,
SelB::Output: SameShape<Sel::Output>,
{
SetOp::new(self.fragment::<IdxA>()).intersect(other)
}
pub fn except<ScopeB, SelB, IdxA, IdxB>(
&self,
other: &Select<D, ScopeB, SelB>,
) -> SetOp<D, Sel::Output>
where
Sel: Selection<Scope, IdxA>,
SelB: Selection<ScopeB, IdxB>,
SelB::Output: SameShape<Sel::Output>,
{
SetOp::new(self.fragment::<IdxA>()).except(other)
}
}