use std::marker::PhantomData;
use crate::cte::Cte;
use crate::dialect::{Dialect, SupportsFullOuterJoin, SupportsRightJoin};
use crate::expr::{BoolLike, Expr, ExprKind, IntoExpr, Value};
use crate::render::{
Fragment, FragmentSink, QuerySink, SelectItem, Sink, render_and_list, render_expr,
render_expr_list, render_order_by, render_select_list,
};
use crate::scope::{
BaseTable, Cons, MapNullable, MaybeNull, Nil, NotNull, ScopeTables, Superset, Table, TableSlot,
};
mod dyn_select;
mod prepared;
mod selection;
mod set_op;
pub use crate::expr::SortDir;
pub use dyn_select::{CannotFilterAfterErase, DynSelect};
pub use prepared::{Prepared, PreparedParams, Total, UnresolvedPlaceholder};
pub use selection::{
All, AllColumns, ColumnList, RowField, SelectableSealed, Selection, SelectionPart, SingleColumn,
};
pub use set_op::SetOp;
#[doc(hidden)]
#[derive(Debug, Clone)]
pub struct CteDef {
name: &'static str,
column_names: Vec<&'static str>,
body: Fragment,
}
impl CteDef {
pub(crate) fn new(name: &'static str, column_names: Vec<&'static str>, body: Fragment) -> Self {
CteDef {
name,
column_names,
body,
}
}
}
#[derive(Debug, Clone)]
enum JoinKind {
Inner,
Left,
Right,
Full,
}
#[derive(Debug, Clone)]
struct JoinClause {
kind: JoinKind,
table: &'static str,
on: ExprKind,
}
pub struct OrderKey<Req> {
kind: ExprKind,
dir: SortDir,
_marker: PhantomData<fn() -> Req>,
}
impl<Req> Clone for OrderKey<Req> {
fn clone(&self) -> Self {
OrderKey {
kind: self.kind.clone(),
dir: self.dir,
_marker: PhantomData,
}
}
}
impl<Req> OrderKey<Req> {
pub(crate) fn into_parts(self) -> (ExprKind, SortDir) {
(self.kind, self.dir)
}
}
pub trait OrderExt: IntoExpr + Sized {
fn asc(self) -> OrderKey<Self::Req> {
OrderKey {
kind: self.into_expr().kind,
dir: SortDir::Asc,
_marker: PhantomData,
}
}
fn sort(self, dir: SortDir) -> OrderKey<Self::Req> {
OrderKey {
kind: self.into_expr().kind,
dir,
_marker: PhantomData,
}
}
fn desc(self) -> OrderKey<Self::Req> {
OrderKey {
kind: self.into_expr().kind,
dir: SortDir::Desc,
_marker: PhantomData,
}
}
}
impl<T: IntoExpr> OrderExt for T {}
pub trait JoinSource<D>: join_source::Sealed {
type Table: Table;
#[doc(hidden)]
fn binding(self) -> Option<CteDef>;
}
mod join_source {
pub trait Sealed {}
impl<T: super::BaseTable> Sealed for T {}
impl<D, Marker> Sealed for crate::cte::Cte<D, Marker> {}
}
impl<D, T: BaseTable> JoinSource<D> for T {
type Table = T;
fn binding(self) -> Option<CteDef> {
None
}
}
impl<D, Marker: crate::cte::CteShape> JoinSource<D> for Cte<D, Marker> {
type Table = Marker;
fn binding(self) -> Option<CteDef> {
Some(CteDef::new(
<Marker as Table>::NAME,
<Marker::Row as crate::row::ColumnNames>::names(),
self.into_body(),
))
}
}
#[diagnostic::on_unimplemented(
message = "`{Self}` isn't a sort key",
label = "a column or expression with `.asc()`/`.desc()`/`.sort(dir)` on it, or a `sort_key(..)`",
note = "a `SortKey` also has to have been discharged against *this* scope — a scope lists its tables most-recently-joined first, so two that look alike can still differ in order"
)]
pub trait SortBy<Scope, Idxs> {
#[doc(hidden)]
fn into_sort_key(self) -> SortKey<Scope>;
}
impl<Scope: Superset<Req, Idxs>, Req, Idxs> SortBy<Scope, Idxs> for OrderKey<Req> {
fn into_sort_key(self) -> SortKey<Scope> {
SortKey {
kind: self.kind,
dir: self.dir,
_marker: PhantomData,
}
}
}
impl<Scope> SortBy<Scope, ()> for SortKey<Scope> {
fn into_sort_key(self) -> SortKey<Scope> {
self
}
}
#[diagnostic::on_unimplemented(
message = "`{Self}` isn't a grouping key",
label = "a column or expression, or a `grouping(..)`",
note = "a `Grouping` also has to have been discharged against *this* scope — a scope lists its tables most-recently-joined first, so two that look alike can still differ in order"
)]
pub trait GroupBy<Scope, Idxs> {
#[doc(hidden)]
fn into_grouping(self) -> Grouping<Scope>;
}
impl<Scope: Superset<Req, Idxs>, Req, Idxs, T: IntoExpr<Req = Req>> GroupBy<Scope, Idxs> for T {
fn into_grouping(self) -> Grouping<Scope> {
Grouping {
kind: self.into_expr().kind,
_marker: PhantomData,
}
}
}
impl<Scope> GroupBy<Scope, ()> for Grouping<Scope> {
fn into_grouping(self) -> Grouping<Scope> {
self
}
}
pub struct SortKey<Scope> {
kind: ExprKind,
dir: SortDir,
_marker: PhantomData<fn() -> Scope>,
}
impl<Scope> Clone for SortKey<Scope> {
fn clone(&self) -> Self {
SortKey {
kind: self.kind.clone(),
dir: self.dir,
_marker: PhantomData,
}
}
}
pub fn sort_key<Scope, Idxs, K: SortBy<Scope, Idxs>>(key: K) -> SortKey<Scope> {
key.into_sort_key()
}
pub struct Grouping<Scope> {
kind: ExprKind,
_marker: PhantomData<fn() -> Scope>,
}
impl<Scope> Clone for Grouping<Scope> {
fn clone(&self) -> Self {
Grouping {
kind: self.kind.clone(),
_marker: PhantomData,
}
}
}
pub fn grouping<Scope, Idxs, K: GroupBy<Scope, Idxs>>(key: K) -> Grouping<Scope> {
key.into_grouping()
}
#[diagnostic::on_unimplemented(
message = "`{Self}` isn't a condition",
label = "a comparison (`.eq(..)`, `.gt(..)`, `.is_null()`), an `any_of`/`all_of` of them, a `sql!` fragment of type `Bool`, a `predicate(..)`, or an `EXISTS` of a subquery in *this* dialect",
note = "a `Predicate` also has to have been discharged against *this* scope — a scope lists its tables most-recently-joined first, so two that look alike can still differ in order"
)]
pub trait Condition<D, Scope, Idxs> {
#[doc(hidden)]
fn into_predicate(self) -> Predicate<D, Scope>;
}
impl<D, Scope: Superset<Req, Idxs>, Req, Idxs, T: IntoExpr<Req = Req>> Condition<D, Scope, Idxs>
for T
where
T::Sql: BoolLike,
{
fn into_predicate(self) -> Predicate<D, Scope> {
Predicate {
kind: self.into_expr().kind,
_marker: PhantomData,
}
}
}
impl<D, Scope> Condition<D, Scope, ()> for Predicate<D, Scope> {
fn into_predicate(self) -> Predicate<D, Scope> {
self
}
}
pub struct Exists<D, Req> {
kind: ExprKind,
_marker: PhantomData<fn() -> (D, Req)>,
}
impl<D, Req> Clone for Exists<D, Req> {
fn clone(&self) -> Self {
Exists {
kind: self.kind.clone(),
_marker: PhantomData,
}
}
}
impl<D, Scope: Superset<Req, Idxs>, Req, Idxs> Condition<D, Scope, Idxs> for Exists<D, Req> {
fn into_predicate(self) -> Predicate<D, Scope> {
Predicate {
kind: self.kind,
_marker: PhantomData,
}
}
}
pub struct Predicate<D, Scope> {
kind: ExprKind,
_marker: PhantomData<fn() -> (D, Scope)>,
}
impl<D, Scope> Clone for Predicate<D, Scope> {
fn clone(&self) -> Self {
Predicate {
kind: self.kind.clone(),
_marker: PhantomData,
}
}
}
impl<D, Scope> Predicate<D, Scope> {
pub fn any_of(preds: impl IntoIterator<Item = Predicate<D, Scope>>) -> Self {
Predicate::combine(preds, false)
}
pub fn all_of(preds: impl IntoIterator<Item = Predicate<D, Scope>>) -> Self {
Predicate::combine(preds, true)
}
fn combine(preds: impl IntoIterator<Item = Predicate<D, Scope>>, all: bool) -> Self {
Predicate {
kind: crate::expr::fold_conditions(preds.into_iter().map(Predicate::into_kind), all),
_marker: PhantomData,
}
}
pub(crate) fn into_kind(self) -> ExprKind {
self.kind
}
}
pub fn predicate<D, Scope, Idxs, C: Condition<D, Scope, Idxs>>(cond: C) -> Predicate<D, Scope> {
cond.into_predicate()
}
pub struct SelectSeed<Sel> {
selection: Sel,
}
pub fn select<Sel>(selection: Sel) -> SelectSeed<Sel> {
SelectSeed { selection }
}
impl<Sel> SelectSeed<Sel> {
pub fn from<D, S: JoinSource<D>>(
self,
source: S,
) -> Select<D, Cons<TableSlot<S::Table, NotNull>, Nil>, Sel> {
let mut body = SelectBody::new(<S::Table as Table>::NAME);
body.bind(source);
Select {
body,
selection: self.selection,
_marker: PhantomData,
}
}
}
#[derive(Debug, Clone)]
pub(crate) struct SelectBody {
ctes: Vec<CteDef>,
distinct: bool,
from_table: &'static str,
joins: Vec<JoinClause>,
wheres: Vec<ExprKind>,
order_by: Vec<(ExprKind, SortDir)>,
group_by: Vec<ExprKind>,
having: Vec<ExprKind>,
limit: Option<RowCount>,
offset: Option<RowCount>,
}
impl SelectBody {
fn new(from_table: &'static str) -> Self {
SelectBody {
ctes: Vec::new(),
distinct: false,
from_table,
joins: Vec::new(),
wheres: Vec::new(),
order_by: Vec::new(),
group_by: Vec::new(),
having: Vec::new(),
limit: None,
offset: None,
}
}
fn count_sql<D: Dialect>(&self, selection: &[SelectItem]) -> (String, Vec<Value>) {
let mut body = self.clone();
body.order_by.clear();
body.limit = None;
body.offset = None;
let one_row_each = body.group_by.is_empty()
&& body.having.is_empty()
&& !body.distinct
&& selection
.iter()
.all(|item| matches!(item.kind, ExprKind::Column { .. }));
let mut sink = QuerySink::<D>::new();
if one_row_each {
body.render_into::<D>(&[crate::expr::count_item()], &mut sink);
return sink.finish();
}
crate::render::render_count_wrapped::<D>(&mut sink, |sink| {
body.render_into::<D>(selection, sink)
});
sink.finish()
}
fn bind<D, S: JoinSource<D>>(&mut self, source: S) {
self.ctes.extend(source.binding());
}
}
pub struct Select<D, Scope, Sel, Outer = Nil> {
body: SelectBody,
selection: Sel,
_marker: PhantomData<fn() -> (D, Scope, Outer)>,
}
impl<D, Scope, Sel: Clone, Outer> Clone for Select<D, Scope, Sel, Outer> {
fn clone(&self) -> Self {
Select {
body: self.body.clone(),
selection: self.selection.clone(),
_marker: PhantomData,
}
}
}
impl<D, Scope, Sel, Outer> Select<D, Scope, Sel, Outer> {
fn retype<NewScope>(self) -> Select<D, NewScope, Sel, Outer> {
Select {
body: self.body,
selection: self.selection,
_marker: PhantomData,
}
}
pub fn reselect<NewSel>(self, selection: NewSel) -> Select<D, Scope, NewSel, Outer> {
Select {
body: self.body,
selection,
_marker: PhantomData,
}
}
pub fn filter<C: Condition<D, Scope, Idxs>, Idxs>(mut self, cond: C) -> Self {
self.body.wheres.push(cond.into_predicate().into_kind());
self
}
pub fn filter_all(mut self, conds: impl IntoIterator<Item = Predicate<D, Scope>>) -> Self {
self.body
.wheres
.extend(conds.into_iter().map(Predicate::into_kind));
self
}
pub fn order_by<K: SortBy<Scope, Idxs>, Idxs>(mut self, key: K) -> Self {
let key = key.into_sort_key();
self.body.order_by.push((key.kind, key.dir));
self
}
pub fn order_by_all(mut self, keys: impl IntoIterator<Item = SortKey<Scope>>) -> Self {
self.body
.order_by
.extend(keys.into_iter().map(|k| (k.kind, k.dir)));
self
}
pub fn distinct(mut self) -> Self {
self.body.distinct = true;
self
}
pub fn group_by<K: GroupBy<Scope, Idxs>, Idxs>(mut self, key: K) -> Self {
self.body.group_by.push(key.into_grouping().kind);
self
}
pub fn group_by_all(mut self, keys: impl IntoIterator<Item = Grouping<Scope>>) -> Self {
self.body.group_by.extend(keys.into_iter().map(|g| g.kind));
self
}
pub fn having<C: Condition<D, Scope, Idxs>, Idxs>(mut self, cond: C) -> Self {
self.body.having.push(cond.into_predicate().into_kind());
self
}
pub fn having_all(mut self, conds: impl IntoIterator<Item = Predicate<D, Scope>>) -> Self {
self.body
.having
.extend(conds.into_iter().map(Predicate::into_kind));
self
}
pub fn limit(mut self, n: impl IntoRowCount) -> Self {
self.body.limit = Some(n.into_row_count());
self
}
pub fn offset(mut self, n: impl IntoRowCount) -> Self {
self.body.offset = Some(n.into_row_count());
self
}
pub fn inner_join<S: JoinSource<D>, C, Idxs>(
mut self,
source: S,
on: C,
) -> Select<D, Cons<TableSlot<S::Table, NotNull>, Scope>, Sel, Outer>
where
C: Condition<D, Cons<TableSlot<S::Table, NotNull>, Scope>, Idxs>,
{
self.body.bind(source);
self.body.joins.push(JoinClause {
kind: JoinKind::Inner,
table: <S::Table as Table>::NAME,
on: on.into_predicate().into_kind(),
});
self.retype()
}
pub fn left_join<S: JoinSource<D>, C, Idxs>(
mut self,
source: S,
on: C,
) -> Select<D, Cons<TableSlot<S::Table, MaybeNull>, Scope>, Sel, Outer>
where
C: Condition<D, Cons<TableSlot<S::Table, MaybeNull>, Scope>, Idxs>,
{
self.body.bind(source);
self.body.joins.push(JoinClause {
kind: JoinKind::Left,
table: <S::Table as Table>::NAME,
on: on.into_predicate().into_kind(),
});
self.retype()
}
pub fn right_join<S: JoinSource<D>, C, Idxs>(
mut self,
source: S,
on: C,
) -> Select<D, Cons<TableSlot<S::Table, NotNull>, Scope::Output>, Sel, Outer>
where
D: SupportsRightJoin,
Scope: MapNullable,
C: Condition<D, Cons<TableSlot<S::Table, NotNull>, Scope::Output>, Idxs>,
{
self.body.bind(source);
self.body.joins.push(JoinClause {
kind: JoinKind::Right,
table: <S::Table as Table>::NAME,
on: on.into_predicate().into_kind(),
});
self.retype()
}
pub fn full_join<S: JoinSource<D>, C, Idxs>(
mut self,
source: S,
on: C,
) -> Select<D, Cons<TableSlot<S::Table, MaybeNull>, Scope::Output>, Sel, Outer>
where
D: SupportsFullOuterJoin,
Scope: MapNullable,
C: Condition<D, Cons<TableSlot<S::Table, MaybeNull>, Scope::Output>, Idxs>,
{
self.body.bind(source);
self.body.joins.push(JoinClause {
kind: JoinKind::Full,
table: <S::Table as Table>::NAME,
on: on.into_predicate().into_kind(),
});
self.retype()
}
}
impl<D: Dialect, Scope, Sel> Select<D, Scope, Sel> {
pub fn to_sql<Idx>(&self, _dialect: D) -> (String, Vec<Value>)
where
Sel: Selection<Scope, Idx>,
{
self.render_as::<Idx>()
}
pub fn count_sql<Idx>(&self, _dialect: D) -> (String, Vec<Value>)
where
Sel: Selection<Scope, Idx>,
{
self.body.count_sql::<D>(&self.selection.items())
}
}
impl<D: Dialect, Scope, Sel> Select<D, Scope, Sel> {
pub(crate) fn fragment<Idx>(&self) -> Fragment
where
Sel: Selection<Scope, Idx>,
{
let mut sink = FragmentSink::new();
self.body
.render_into::<D>(&self.selection.items(), &mut sink);
sink.finish()
}
fn render_as<Idx>(&self) -> (String, Vec<Value>)
where
Sel: Selection<Scope, Idx>,
{
let mut sink = QuerySink::<D>::new();
self.body
.render_into::<D>(&self.selection.items(), &mut sink);
sink.finish()
}
}
impl SelectBody {
pub(crate) fn render_into<D: Dialect>(&self, selection: &[SelectItem], sink: &mut dyn Sink) {
let SelectBody {
ctes,
distinct,
from_table,
joins,
wheres,
order_by,
group_by,
having,
limit,
offset,
} = self;
if !ctes.is_empty() {
sink.text("WITH ");
for (i, cte) in ctes.iter().enumerate() {
if i > 0 {
sink.text(", ");
}
crate::render::render_ident::<D>(sink, cte.name);
sink.text(" (");
for (i, col) in cte.column_names.iter().enumerate() {
if i > 0 {
sink.text(", ");
}
crate::render::render_ident::<D>(sink, col);
}
sink.text(") AS (");
cte.body.splice_into(sink);
sink.ch(')');
}
sink.ch(' ');
}
sink.text("SELECT ");
if *distinct {
sink.text("DISTINCT ");
}
render_select_list::<D>(selection, sink);
sink.text(" FROM ");
crate::render::render_ident::<D>(sink, from_table);
for j in joins {
sink.ch(' ');
sink.text(match j.kind {
JoinKind::Inner => "INNER JOIN",
JoinKind::Left => "LEFT JOIN",
JoinKind::Right => "RIGHT JOIN",
JoinKind::Full => "FULL JOIN",
});
sink.ch(' ');
crate::render::render_ident::<D>(sink, j.table);
sink.text(" ON ");
render_expr::<D>(&j.on, sink);
}
render_and_list::<D>(sink, " WHERE ", wheres);
render_expr_list::<D>(sink, " GROUP BY ", group_by);
render_and_list::<D>(sink, " HAVING ", having);
render_order_by::<D>(sink, " ORDER BY ", order_by);
render_limit_offset::<D>(sink, limit.as_ref(), offset.as_ref());
}
}
pub(crate) fn correlated_with<D, Scope, S: JoinSource<D>, InnerSel>(
source: S,
selection: InnerSel,
) -> Select<D, Cons<TableSlot<S::Table, NotNull>, Scope>, InnerSel, Scope> {
let mut body = SelectBody::new(<S::Table as Table>::NAME);
body.bind(source);
Select {
body,
selection,
_marker: PhantomData,
}
}
impl<D, Scope, Sel, Outer> Select<D, Scope, Sel, Outer> {
pub fn correlated<S: JoinSource<D>, InnerSel>(
&self,
source: S,
selection: InnerSel,
) -> Select<D, Cons<TableSlot<S::Table, NotNull>, Scope>, InnerSel, Scope> {
correlated_with(source, selection)
}
}
impl<D: Dialect, Scope, Sel, Outer: ScopeTables> Select<D, Scope, Sel, Outer> {
pub fn exists<Idx>(&self) -> Exists<D, Outer::Tables>
where
Sel: Selection<Scope, Idx>,
{
self.exists_kind::<Idx>(false)
}
pub fn not_exists<Idx>(&self) -> Exists<D, Outer::Tables>
where
Sel: Selection<Scope, Idx>,
{
self.exists_kind::<Idx>(true)
}
fn exists_kind<Idx>(&self, negated: bool) -> Exists<D, Outer::Tables>
where
Sel: Selection<Scope, Idx>,
{
Exists {
kind: ExprKind::Exists {
body: Box::new(self.body.clone()),
selection: self.selection.items(),
negated,
},
_marker: PhantomData,
}
}
}
#[diagnostic::on_unimplemented(
message = "`{Self}` isn't a number of rows",
label = "an integer, or a `prepare!{{}}` placeholder of type `Integer`/`BigInt`"
)]
pub trait IntoRowCount {
fn into_row_count(self) -> RowCount;
}
#[derive(Debug, Clone)]
pub struct RowCount(RowCountKind);
#[derive(Debug, Clone)]
enum RowCountKind {
Literal(i64),
Bound(ExprKind),
}
macro_rules! into_row_count {
($($signed:ty),+ ; $($unsigned:ty),+) => {
$(impl IntoRowCount for $signed {
fn into_row_count(self) -> RowCount {
RowCount(RowCountKind::Literal((self as i64).max(0)))
}
})+
$(impl IntoRowCount for $unsigned {
fn into_row_count(self) -> RowCount {
RowCount(RowCountKind::Literal(i64::try_from(self).unwrap_or(i64::MAX)))
}
})+
};
}
into_row_count!(i8, i16, i32, i64, isize ; u8, u16, u32, u64, usize);
impl IntoRowCount for Expr<Nil, crate::expr::Integer> {
fn into_row_count(self) -> RowCount {
RowCount(RowCountKind::Bound(self.kind))
}
}
impl IntoRowCount for Expr<Nil, crate::expr::BigInt> {
fn into_row_count(self) -> RowCount {
RowCount(RowCountKind::Bound(self.kind))
}
}
pub(crate) fn render_limit_offset<D: Dialect>(
sink: &mut dyn Sink,
limit: Option<&RowCount>,
offset: Option<&RowCount>,
) {
match (limit, offset, D::OFFSET_WITHOUT_LIMIT) {
(Some(l), _, _) => {
sink.text(" LIMIT ");
render_row_count::<D>(sink, l);
}
(None, Some(_), Some(filler)) => {
sink.text(" LIMIT ");
sink.text(filler);
}
_ => {}
}
if let Some(o) = offset {
sink.text(" OFFSET ");
render_row_count::<D>(sink, o);
}
}
fn render_row_count<D: Dialect>(sink: &mut dyn Sink, count: &RowCount) {
match &count.0 {
RowCountKind::Literal(n) => sink.text(&n.to_string()),
RowCountKind::Bound(kind) => render_expr::<D>(kind, sink),
}
}