use core::marker::PhantomData;
use crate::relation::RelationDef;
use crate::{PaginationArg, SQL, SQLParam};
use super::builder::{
AllColumns, Clauses, HasLimit, HasOffset, HasOrderBy, HasWhere, IntoColumnSelection, NoLimit,
NoOrderBy, NoWhere, PartialColumns, QueryTable,
};
pub struct RelationHandle<
'a,
V: SQLParam,
R: RelationDef,
Nested = (),
Cols = AllColumns,
Cl = Clauses,
> {
pub(crate) where_sql: SQL<'a, V>,
pub(crate) order_by_sql: SQL<'a, V>,
pub(crate) limit: Option<SQL<'a, V>>,
pub(crate) offset: Option<SQL<'a, V>>,
pub(crate) nested: Nested,
pub(crate) cols: Cols,
pub(crate) _marker: PhantomData<(R, Cl)>,
}
impl<'a, V: SQLParam, R: RelationDef> RelationHandle<'a, V, R> {
#[must_use]
pub const fn new() -> Self {
Self {
where_sql: SQL::empty(),
order_by_sql: SQL::empty(),
limit: None,
offset: None,
nested: (),
cols: AllColumns,
_marker: PhantomData,
}
}
}
impl<'a, V: SQLParam, R: RelationDef> Default for RelationHandle<'a, V, R> {
fn default() -> Self {
Self::new()
}
}
impl<'a, V: SQLParam, R: RelationDef, Nested, Cols, Cl> RelationHandle<'a, V, R, Nested, Cols, Cl> {
#[allow(clippy::type_complexity)]
pub fn with<NR, NN, NC, NCl>(
self,
handle: RelationHandle<'a, V, NR, NN, NC, NCl>,
) -> RelationHandle<'a, V, R, (RelationHandle<'a, V, NR, NN, NC, NCl>, Nested), Cols, Cl>
where
NR: RelationDef<Source = R::Target> + 'static,
{
RelationHandle {
where_sql: self.where_sql,
order_by_sql: self.order_by_sql,
limit: self.limit,
offset: self.offset,
nested: (handle, self.nested),
cols: self.cols,
_marker: PhantomData,
}
}
}
impl<'a, V: SQLParam, R: RelationDef, Nested, Cols, Ord, Lim>
RelationHandle<'a, V, R, Nested, Cols, Clauses<NoWhere, Ord, Lim>>
{
pub fn r#where<E, ScopeProof>(
self,
condition: E,
) -> RelationHandle<'a, V, R, Nested, Cols, Clauses<HasWhere, Ord, Lim>>
where
R::Card: crate::relation::Filtered,
E: crate::expr::Expr<'a, V>,
E::Sources: crate::scope::SourcesIn<crate::Cons<R::Target, crate::Nil>, ScopeProof>,
E::SQLType: crate::types::BooleanLike,
V: 'a,
{
RelationHandle {
where_sql: condition.into_expr_sql(),
order_by_sql: self.order_by_sql,
limit: self.limit,
offset: self.offset,
nested: self.nested,
cols: self.cols,
_marker: PhantomData,
}
}
}
impl<'a, V: SQLParam, R: RelationDef, Nested, Cols, W, Lim>
RelationHandle<'a, V, R, Nested, Cols, Clauses<W, NoOrderBy, Lim>>
{
pub fn order_by<E, ScopeProof>(
self,
expr: E,
) -> RelationHandle<'a, V, R, Nested, Cols, Clauses<W, HasOrderBy, Lim>>
where
E: crate::traits::ToSQL<'a, V> + crate::expr::ExprSources,
E::Sources: crate::scope::SourcesIn<crate::Cons<R::Target, crate::Nil>, ScopeProof>,
V: 'a,
{
RelationHandle {
where_sql: self.where_sql,
order_by_sql: expr.to_sql(),
limit: self.limit,
offset: self.offset,
nested: self.nested,
cols: self.cols,
_marker: PhantomData,
}
}
}
impl<'a, V: SQLParam, R: RelationDef, Nested, Cols, W, Ord>
RelationHandle<'a, V, R, Nested, Cols, Clauses<W, Ord, NoLimit>>
{
pub fn limit<P>(self, n: P) -> RelationHandle<'a, V, R, Nested, Cols, Clauses<W, Ord, HasLimit>>
where
R::Card: crate::relation::Paginated,
P: PaginationArg<'a, V>,
V: 'a,
{
RelationHandle {
where_sql: self.where_sql,
order_by_sql: self.order_by_sql,
limit: Some(n.into_pagination_sql()),
offset: self.offset,
nested: self.nested,
cols: self.cols,
_marker: PhantomData,
}
}
pub fn first(self) -> RelationHandle<'a, V, R, Nested, Cols, Clauses<W, Ord, HasLimit>>
where
R::Card: crate::relation::Paginated,
{
self.limit(1u32)
}
}
impl<'a, V: SQLParam, R: RelationDef, Nested, Cols, W, Ord>
RelationHandle<'a, V, R, Nested, Cols, Clauses<W, Ord, HasLimit>>
{
pub fn offset<P>(
self,
n: P,
) -> RelationHandle<'a, V, R, Nested, Cols, Clauses<W, Ord, HasOffset>>
where
P: PaginationArg<'a, V>,
V: 'a,
{
RelationHandle {
where_sql: self.where_sql,
order_by_sql: self.order_by_sql,
limit: self.limit,
offset: Some(n.into_pagination_sql()),
nested: self.nested,
cols: self.cols,
_marker: PhantomData,
}
}
}
impl<'a, V: SQLParam, R: RelationDef, Nested, Cl> RelationHandle<'a, V, R, Nested, AllColumns, Cl>
where
R::Target: QueryTable,
{
pub fn columns<S: IntoColumnSelection>(
self,
selector: S,
) -> RelationHandle<'a, V, R, Nested, PartialColumns, Cl> {
RelationHandle {
where_sql: self.where_sql,
order_by_sql: self.order_by_sql,
limit: self.limit,
offset: self.offset,
nested: self.nested,
cols: PartialColumns {
columns: selector.into_column_names(),
},
_marker: PhantomData,
}
}
pub fn omit<S: IntoColumnSelection>(
self,
selector: S,
) -> RelationHandle<'a, V, R, Nested, PartialColumns, Cl> {
let omitted = selector.into_column_names();
let columns = <R::Target as QueryTable>::COLUMN_NAMES
.iter()
.copied()
.filter(|c| !omitted.contains(c))
.collect();
RelationHandle {
where_sql: self.where_sql,
order_by_sql: self.order_by_sql,
limit: self.limit,
offset: self.offset,
nested: self.nested,
cols: PartialColumns { columns },
_marker: PhantomData,
}
}
}
impl<'a, V, R, Cols, Cl> RelationHandle<'a, V, R, (), Cols, Cl>
where
V: SQLParam + 'a,
R: RelationDef,
R::Source: QueryTable,
{
fn subquery_sql(self, select: SQL<'a, V>) -> SQL<'a, V> {
super::sql::relation_subquery_sql(
select,
<R::Target as QueryTable>::TABLE,
<R::Source as QueryTable>::TABLE_NAME,
R::NAME,
R::junction(),
R::fk_columns(),
self.where_sql,
self.order_by_sql,
self.limit,
self.offset,
)
}
#[allow(clippy::type_complexity)]
pub fn select<E, ScopeProof>(
self,
expr: E,
) -> crate::expr::SQLExpr<
'a,
V,
E::SQLType,
crate::expr::Null,
crate::expr::Scalar,
crate::Src<R::Source>,
>
where
E: crate::expr::Expr<'a, V>,
E::Sources: crate::scope::SourcesIn<crate::Cons<R::Target, crate::Nil>, ScopeProof>,
{
crate::expr::SQLExpr::new(self.subquery_sql(expr.into_expr_sql()).parens())
}
}
impl<'a, V, R, Cols, Cl> crate::ToSQL<'a, V> for RelationHandle<'a, V, R, (), Cols, Cl>
where
V: SQLParam + 'a,
R: RelationDef,
R::Source: QueryTable,
{
fn to_sql(&self) -> SQL<'a, V> {
RelationHandle::<'a, V, R, (), AllColumns, Cl> {
where_sql: self.where_sql.clone(),
order_by_sql: self.order_by_sql.clone(),
limit: self.limit.clone(),
offset: self.offset.clone(),
nested: (),
cols: AllColumns,
_marker: PhantomData,
}
.into_sql()
}
fn into_sql(self) -> SQL<'a, V> {
self.subquery_sql(SQL::raw("1"))
}
}
impl<'a, V, R, Cols, Cl> crate::expr::ExprSources for RelationHandle<'a, V, R, (), Cols, Cl>
where
V: SQLParam,
R: RelationDef,
{
type Sources = crate::Src<R::Source>;
}
impl<'a, V, R, Cols, Cl> crate::expr::SelectQuery for RelationHandle<'a, V, R, (), Cols, Cl>
where
V: SQLParam,
R: RelationDef,
{
}