#![feature(unboxed_closures)]
#![allow(internal_features)]
#![cfg_attr(any(docsrs, docsrs_dep), feature(rustdoc_internals))]
pub mod is_nullable;
use std::{
borrow::Cow,
marker::PhantomData,
sync::{Arc, atomic::AtomicU32},
};
use bytemuck::TransparentWrapper as _;
use sea_query::{ExprTrait, OverStatement};
#[derive(Debug, Clone, Copy, PartialEq, PartialOrd, Ord, Eq, Hash)]
struct Binder(u32);
static BINDER_COUNT: AtomicU32 = AtomicU32::new(0);
impl Binder {
fn new() -> Self {
Self(BINDER_COUNT.fetch_add(1, std::sync::atomic::Ordering::SeqCst))
}
#[allow(unused)]
fn reset() {
BINDER_COUNT.store(0, std::sync::atomic::Ordering::SeqCst);
}
}
#[derive(Clone, Eq, PartialEq)]
struct TableName {
binder: Binder,
name: String,
}
impl std::fmt::Debug for TableName {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{}", self.name)
}
}
impl<'a> From<TableName> for Cow<'a, str> {
fn from(val: TableName) -> Self {
format!("t{}", val.binder.0).into()
}
}
impl TableName {
fn new(binder: Binder) -> Self {
Self {
binder,
name: format!("t{}", binder.0),
}
}
}
impl sea_query::Iden for TableName {
fn unquoted(&self) -> &str {
&self.name
}
}
#[derive(Clone, PartialEq, Eq, Hash)]
struct ColumnName {
name: String,
rendered: String,
}
impl std::fmt::Display for ColumnName {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{}", self.rendered)
}
}
impl std::fmt::Debug for ColumnName {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{}", self.rendered)
}
}
impl sea_query::Iden for ColumnName {
fn unquoted(&self) -> &str {
&self.rendered
}
}
impl ColumnName {
fn new(binder: Binder, name: String) -> Self {
Self {
rendered: format!("{}{}", name, binder.0),
name,
}
}
}
pub trait TableHKT {
type Mode: TableMode;
type InMode<Mode: TableMode>;
}
#[cfg(feature = "sqlx")]
mod value_sqlx {
pub trait Value: for<'r> sqlx::Decode<'r, sqlx::Postgres> + sqlx::Type<sqlx::Postgres> {}
impl<T> Value for T where T: for<'r> sqlx::Decode<'r, sqlx::Postgres> + sqlx::Type<sqlx::Postgres> {}
}
#[cfg(not(feature = "sqlx"))]
mod value_sqlx {
pub trait Value {}
impl<T> Value for T where T: ?Sized {}
}
pub use value_sqlx::Value as SqlxValueIfEnabled;
pub trait Value: Into<sea_query::Value> + SqlxValueIfEnabled + IsNullable {}
impl<T> Value for T where T: Into<sea_query::Value> + value_sqlx::Value + IsNullable {}
pub trait ModeMapper<'scope, SrcMode: TableMode, DestMode: TableMode> {
fn map_mode<V>(&mut self, src: SrcMode::T<'scope, V>) -> DestMode::T<'scope, V>
where
V: Value;
}
pub trait ModeMapperRef<'scope, SrcMode: TableMode, DestMode: TableMode> {
fn map_mode_ref<V>(&mut self, src: &SrcMode::T<'scope, V>) -> DestMode::T<'scope, V>
where
V: Value;
}
pub trait ModeMapperMut<'scope, SrcMode: TableMode, DestMode: TableMode> {
fn map_mode_mut<V>(&mut self, src: &mut SrcMode::T<'scope, V>) -> DestMode::T<'scope, V>
where
V: Value;
}
pub trait MapTable<'scope>: TableHKT {
fn map_modes<Mapper, DestMode>(self, mapper: &mut Mapper) -> Self::InMode<DestMode>
where
Mapper: ModeMapper<'scope, Self::Mode, DestMode>,
DestMode: TableMode;
fn map_modes_ref<Mapper, DestMode>(&self, mapper: &mut Mapper) -> Self::InMode<DestMode>
where
Mapper: ModeMapperRef<'scope, Self::Mode, DestMode>,
DestMode: TableMode;
fn map_modes_mut<Mapper, DestMode>(&mut self, mapper: &mut Mapper) -> Self::InMode<DestMode>
where
Mapper: ModeMapperMut<'scope, Self::Mode, DestMode>,
DestMode: TableMode;
}
struct NameToExprMapper {
binder: Binder,
query: sea_query::SelectStatement,
}
impl<'scope> ModeMapperRef<'scope, NameMode, ExprMode> for NameToExprMapper {
fn map_mode_ref<V>(
&mut self,
src: &<NameMode as TableMode>::T<'scope, V>,
) -> <ExprMode as TableMode>::T<'scope, V> {
let col_name = ColumnName::new(self.binder, src.to_string());
self.query
.expr_as(sea_query::Expr::column(*src), col_name.clone());
Expr::new(ExprInner::Column(TableName::new(self.binder), col_name))
}
}
struct LitMapper {}
impl<'scope> ModeMapper<'scope, ValueMode, ExprMode> for LitMapper {
fn map_mode<V>(
&mut self,
src: <ValueMode as TableMode>::T<'scope, V>,
) -> <ExprMode as TableMode>::T<'scope, V>
where
V: Value,
{
Expr::new(ExprInner::Raw(sea_query::Expr::value(src.into())))
}
}
struct ExprCollectorMapper {
idx: usize,
table_binder: Binder,
columns: Vec<ColumnName>,
values: Vec<sea_query::Value>,
}
impl<'scope> ModeMapper<'scope, ValueMode, ExprMode> for ExprCollectorMapper {
fn map_mode<V>(
&mut self,
src: <ValueMode as TableMode>::T<'scope, V>,
) -> <ExprMode as TableMode>::T<'scope, V>
where
V: Value,
{
let idx = self.idx;
self.idx += 1;
self.values.push(src.into());
let column_name = ColumnName::new(self.table_binder, format!("values_{idx}_"));
self.columns.push(column_name.clone());
Expr::new(ExprInner::Column(
TableName::new(self.table_binder),
column_name,
))
}
}
struct ExprCollectorRemainingMapper {
values: Vec<sea_query::Value>,
}
impl<'scope> ModeMapper<'scope, ValueMode, EmptyMode> for ExprCollectorRemainingMapper {
fn map_mode<V>(
&mut self,
src: <ValueMode as TableMode>::T<'scope, V>,
) -> <EmptyMode as TableMode>::T<'scope, V>
where
V: Value,
{
self.values.push(src.into());
}
}
struct NameCollectorMapper {
names: Vec<&'static str>,
}
impl<'scope> ModeMapperRef<'scope, NameMode, EmptyMode> for NameCollectorMapper {
fn map_mode_ref<V>(
&mut self,
src: &<NameMode as TableMode>::T<'scope, V>,
) -> <EmptyMode as TableMode>::T<'scope, V> {
self.names.push(*src);
}
}
struct VisitTableMapper<'a, F> {
f: &'a mut F,
mode: VisitTableMode,
}
impl<'a, 'scope, F> ModeMapperRef<'scope, ExprMode, EmptyMode> for VisitTableMapper<'a, F>
where
F: FnMut(&ErasedExpr),
{
fn map_mode_ref<V>(
&mut self,
src: &<ExprMode as TableMode>::T<'scope, V>,
) -> <EmptyMode as TableMode>::T<'scope, V>
where
V: Value,
{
match self.mode {
VisitTableMode::All => (self.f)(src.as_erased()),
VisitTableMode::NonNull => {
if !<V as IsNullable>::IS_NULLABLE {
(self.f)(src.as_erased());
}
}
}
}
}
struct VisitTableMapperMut<'a, F> {
f: &'a mut F,
mode: VisitTableMode,
}
impl<'a, 'scope, F> ModeMapperMut<'scope, ExprMode, EmptyMode> for VisitTableMapperMut<'a, F>
where
F: FnMut(&mut ErasedExpr),
{
fn map_mode_mut<V>(
&mut self,
src: &mut <ExprMode as TableMode>::T<'scope, V>,
) -> <EmptyMode as TableMode>::T<'scope, V>
where
V: Value,
{
match self.mode {
VisitTableMode::All => (self.f)(src.as_erased_mut()),
VisitTableMode::NonNull => {
if !<V as IsNullable>::IS_NULLABLE {
(self.f)(src.as_erased_mut());
}
}
}
}
}
#[cfg(feature = "sqlx")]
struct LoadingMapper<'a, IT> {
it: &'a mut IT,
}
#[cfg(feature = "sqlx")]
impl<'a, 'b, 'scope, IT> ModeMapperRef<'scope, ExprMode, ValueMode> for LoadingMapper<'a, IT>
where
IT: Iterator<Item = sqlx::postgres::PgValueRef<'b>>,
{
fn map_mode_ref<V>(
&mut self,
_src: &<ExprMode as TableMode>::T<'scope, V>,
) -> <ValueMode as TableMode>::T<'scope, V>
where
V: Value,
{
<_ as sqlx::Decode<sqlx::Postgres>>::decode(self.it.next().unwrap()).unwrap()
}
}
#[cfg(feature = "sqlx")]
struct LoadingManyMapper<'a, IT> {
it: &'a mut IT,
}
#[cfg(feature = "sqlx")]
impl<'a, 'b, 'scope, IT> ModeMapperRef<'scope, ExprMode, ValueManyMode>
for LoadingManyMapper<'a, IT>
where
IT: Iterator<Item = sqlx::postgres::PgValueRef<'b>>,
{
fn map_mode_ref<V>(
&mut self,
_src: &<ExprMode as TableMode>::T<'scope, V>,
) -> <ValueManyMode as TableMode>::T<'scope, V>
where
V: Value,
{
<Vec<V> as sqlx::Decode<sqlx::Postgres>>::decode(self.it.next().unwrap()).unwrap()
}
}
#[cfg(feature = "sqlx")]
struct SkippingMapper<'a, IT> {
it: &'a mut IT,
}
#[cfg(feature = "sqlx")]
impl<'a, 'b, 'scope, IT> ModeMapperRef<'scope, ExprMode, EmptyMode> for SkippingMapper<'a, IT>
where
IT: Iterator<Item = sqlx::postgres::PgValueRef<'b>>,
{
fn map_mode_ref<V>(
&mut self,
_src: &<ExprMode as TableMode>::T<'scope, V>,
) -> <EmptyMode as TableMode>::T<'scope, V> {
self.it.next().unwrap();
}
}
#[cfg(feature = "sqlx")]
struct ManyRemainingMapper {
is_empty: bool,
}
#[cfg(feature = "sqlx")]
impl<'scope> ModeMapperRef<'scope, ValueManyMode, EmptyMode> for ManyRemainingMapper {
fn map_mode_ref<V>(
&mut self,
src: &<ValueManyMode as TableMode>::T<'scope, V>,
) -> <EmptyMode as TableMode>::T<'scope, V>
where
V: Value,
{
self.is_empty &= src.is_empty();
}
}
#[cfg(feature = "sqlx")]
struct SpreadingManyMapper {}
#[cfg(feature = "sqlx")]
impl<'a, 'scope> ModeMapperMut<'scope, ValueManyMode, ValueMode> for SpreadingManyMapper {
fn map_mode_mut<V>(
&mut self,
src: &mut <ValueManyMode as TableMode>::T<'scope, V>,
) -> <ValueMode as TableMode>::T<'scope, V>
where
V: Value,
{
src.pop().unwrap()
}
}
pub trait LitTable: TableHKT<Mode = ValueMode> {
fn lit(self) -> Self::InMode<ExprMode>;
}
impl<'scope, T: TableHKT<Mode = ValueMode> + MapTable<'scope>> LitTable for T {
fn lit(self) -> Self::InMode<ExprMode> {
self.map_modes(&mut LitMapper {})
}
}
pub struct TableSchema<Table> {
pub name: &'static str,
pub columns: Table,
}
pub mod table_modes {
pub enum NameMode {}
#[derive(Debug, PartialEq)]
pub enum ValueMode {}
pub enum ValueManyMode {}
pub enum ExprMode {}
pub enum EmptyMode {}
}
pub use table_modes::*;
pub trait TableMode {
type T<'scope, V>;
}
impl TableMode for NameMode {
type T<'scope, V> = &'static str;
}
impl TableMode for ValueMode {
type T<'scope, V> = V;
}
impl TableMode for ValueManyMode {
type T<'scope, V> = Vec<V>;
}
impl TableMode for ExprMode {
type T<'scope, V> = Expr<'scope, V>;
}
impl TableMode for EmptyMode {
type T<'scope, V> = ();
}
#[derive(bytemuck::TransparentWrapper)]
#[repr(transparent)]
pub struct TableUsingMapper<T>(pub T);
#[allow(missing_docs)]
impl<T> TableUsingMapper<T> {
pub fn wrap(t: T) -> Self {
<Self as bytemuck::TransparentWrapper<T>>::wrap(t)
}
pub fn wrap_ref(t: &T) -> &Self {
<Self as bytemuck::TransparentWrapper<T>>::wrap_ref(t)
}
pub fn wrap_mut(t: &mut T) -> &mut Self {
<Self as bytemuck::TransparentWrapper<T>>::wrap_mut(t)
}
}
impl<'scope, T> Table<'scope> for TableUsingMapper<T>
where
T: Table<'scope> + MapTable<'scope> + TableHKT<Mode = ExprMode>,
{
type Result = T::InMode<ValueMode>;
fn visit(&self, f: &mut impl FnMut(&ErasedExpr), mode: VisitTableMode) {
let mut mapper = VisitTableMapper { f, mode };
self.0.map_modes_ref(&mut mapper);
}
fn visit_mut(&mut self, f: &mut impl FnMut(&mut ErasedExpr), mode: VisitTableMode) {
let mut mapper = VisitTableMapperMut { f, mode };
self.0.map_modes_mut(&mut mapper);
}
}
#[cfg(feature = "sqlx")]
impl<T> TableLoaderSqlx for TableUsingMapper<T>
where
T: Table<'static> + MapTable<'static> + TableHKT<Mode = ExprMode>,
{
fn load<'a>(
&self,
values: &mut impl Iterator<Item = sqlx::postgres::PgValueRef<'a>>,
) -> Self::Result {
let mut mapper = LoadingMapper { it: values };
self.0.map_modes_ref(&mut mapper)
}
fn skip<'a>(&self, values: &mut impl Iterator<Item = sqlx::postgres::PgValueRef<'a>>) {
let mut mapper = SkippingMapper { it: values };
self.0.map_modes_ref(&mut mapper);
}
}
#[cfg(feature = "sqlx")]
impl<T> TableLoaderManySqlx for TableUsingMapper<T>
where
T: Table<'static> + MapTable<'static> + TableHKT<Mode = ExprMode>,
T::InMode<ValueManyMode>: MapTable<'static> + TableHKT<Mode = ValueManyMode>,
<<T as TableHKT>::InMode<table_modes::ValueManyMode> as TableHKT>::InMode<
table_modes::ValueMode,
>: type_equalities::IsEqual<Self::Result>,
{
fn load_many<'a>(
&self,
values: &mut impl Iterator<Item = sqlx::postgres::PgValueRef<'a>>,
) -> Vec<Self::Result> {
let mut mapper = LoadingManyMapper { it: values };
let mut collected = self.0.map_modes_ref(&mut mapper);
let mut results: Vec<Self::Result> = vec![];
while {
let mut mapper = ManyRemainingMapper { is_empty: true };
collected.map_modes_ref(&mut mapper);
!mapper.is_empty
} {
let mut mapper = SpreadingManyMapper {};
results.push(type_equalities::coerce(
collected.map_modes_mut(&mut mapper),
type_equalities::trivial_eq(),
));
}
results
}
}
#[derive(Debug, Copy, Clone)]
pub enum VisitTableMode {
All,
NonNull,
}
pub trait Table<'scope> {
type Result;
fn visit(&self, f: &mut impl FnMut(&ErasedExpr), mode: VisitTableMode);
fn visit_mut(&mut self, f: &mut impl FnMut(&mut ErasedExpr), mode: VisitTableMode);
}
#[cfg(feature = "sqlx")]
pub trait TableLoaderSqlx: Table<'static> {
fn load<'a>(
&self,
values: &mut impl Iterator<Item = sqlx::postgres::PgValueRef<'a>>,
) -> Self::Result;
fn skip<'a>(&self, values: &mut impl Iterator<Item = sqlx::postgres::PgValueRef<'a>>);
}
#[cfg(feature = "sqlx")]
pub trait TableLoaderManySqlx: Table<'static> + TableLoaderSqlx {
fn load_many<'a>(
&self,
values: &mut impl Iterator<Item = sqlx::postgres::PgValueRef<'a>>,
) -> Vec<Self::Result>;
}
fn subst_table<'scope, T: Table<'scope>>(
table: &mut T,
table_name: TableName,
dest_select: &mut sea_query::SelectStatement,
) {
table.visit_mut(
&mut |ErasedExpr(inner)| {
let new_column_name = match inner {
ExprInner::Raw(..) => ColumnName::new(Binder::new(), "lit".to_owned()),
ExprInner::Column(_table_name, column_name) => {
ColumnName::new(Binder::new(), column_name.name.clone())
}
ExprInner::BinOp(..) => ColumnName::new(Binder::new(), "expr".to_owned()),
ExprInner::NOp(..) => ColumnName::new(Binder::new(), "expr".to_owned()),
};
let r = inner.render();
dest_select.expr_as(r, new_column_name.clone());
*inner = ExprInner::Column(table_name.clone(), new_column_name);
},
VisitTableMode::All,
)
}
fn insert_table_name<'scope, T: Table<'scope>>(table: &mut T, new_table_name: TableName) {
table.visit_mut(
&mut |ErasedExpr(inner)| match inner {
ExprInner::Raw(_) => {}
ExprInner::Column(table_name, _column_name) => {
*table_name = new_table_name.clone();
}
ExprInner::BinOp(_, expr_inner, expr_inner1) => {
expr_inner.visit_mut(&mut |table_name, _| *table_name = new_table_name.clone());
expr_inner1.visit_mut(&mut |table_name, _| *table_name = new_table_name.clone());
}
ExprInner::NOp(_, expr_inners) => {
for expr_inner in expr_inners {
expr_inner.visit_mut(&mut |table_name, _| *table_name = new_table_name.clone());
}
}
},
VisitTableMode::All,
)
}
fn collect_exprs<'scope, T: Table<'scope>>(table: &T) -> Vec<ExprInner> {
let mut exprs = vec![];
table.visit(
&mut |ErasedExpr(e)| {
exprs.push(e.clone());
},
VisitTableMode::All,
);
exprs
}
#[non_exhaustive]
pub struct WithLtMarker {}
impl WithLtMarker {
fn new() -> Self {
Self {}
}
}
pub trait ForLifetimeTable {
type WithLt<'lt>: ForLifetimeTable + Table<'lt> + Sized;
fn with_lt<'lt>(self, marker: &mut WithLtMarker) -> Self::WithLt<'lt>;
}
impl<'scope, T> Table<'scope> for Expr<'scope, T>
where
T: Value,
{
type Result = T;
fn visit(&self, f: &mut impl FnMut(&ErasedExpr), mode: VisitTableMode) {
match mode {
VisitTableMode::All => f(self.as_erased()),
VisitTableMode::NonNull => {
if !<T as IsNullable>::IS_NULLABLE {
f(self.as_erased());
}
}
}
}
fn visit_mut(&mut self, f: &mut impl FnMut(&mut ErasedExpr), mode: VisitTableMode) {
match mode {
VisitTableMode::All => f(self.as_erased_mut()),
VisitTableMode::NonNull => {
if !<T as IsNullable>::IS_NULLABLE {
f(self.as_erased_mut());
}
}
}
}
}
impl<'scope, T: Value> ForLifetimeTable for Expr<'scope, T> {
type WithLt<'lt> = Expr<'lt, T>;
fn with_lt<'lt>(self, _marker: &mut WithLtMarker) -> Self::WithLt<'lt> {
Expr::new(self.expr)
}
}
impl<'scope, T> ShortenLifetime for Expr<'scope, T> {
type Shortened<'small>
= Expr<'small, T>
where
Self: 'small;
fn shorten_lifetime<'small, 'large: 'small>(self) -> Self::Shortened<'small>
where
Self: 'large,
{
Expr::new(self.expr)
}
}
#[cfg(feature = "sqlx")]
impl<T: Value> TableLoaderSqlx for Expr<'static, T> {
fn load<'a>(
&self,
values: &mut impl Iterator<Item = sqlx::postgres::PgValueRef<'a>>,
) -> Self::Result {
T::decode(values.next().unwrap()).unwrap()
}
fn skip<'a>(&self, values: &mut impl Iterator<Item = sqlx::postgres::PgValueRef<'a>>) {
let _ = values.next().unwrap();
}
}
#[cfg(feature = "sqlx")]
impl<T: Value> TableLoaderManySqlx for Expr<'static, T> {
fn load_many<'a>(
&self,
values: &mut impl Iterator<Item = sqlx::postgres::PgValueRef<'a>>,
) -> Vec<Self::Result> {
<Vec<T> as sqlx::Decode<'_, sqlx::Postgres>>::decode(values.next().unwrap())
.unwrap_or_default()
}
}
macro_rules! izip_priv {
( @closure $p:pat => $tup:expr ) => {
|$p| $tup
};
( @closure $p:pat => ( $($tup:tt)* ) , $_iter:expr $( , $tail:expr )* ) => {
izip_priv!(@closure ($p, b) => ( $($tup)*, b ) $( , $tail )*)
};
($first:expr $(,)*) => {
$first.into_iter().map(|a| (a,))
};
( $first:expr $( , $rest:expr )* $(,)* ) => {
{
let iter = $first.into_iter();
$(
let iter = iter.zip($rest);
)*
iter.map(izip_priv!(@closure a => (a) $( , $rest )*))
}
};
}
macro_rules! impl_tuples {
($(#[$meta:meta])* $(($idx:tt, $ty:ident)),*) => {
$(#[$meta])*
impl<'scope, $($ty,)*> Table<'scope> for ($($ty,)*)
where $($ty: Table<'scope>,)*
{
type Result = ($($ty::Result,)*);
fn visit(&self, f: &mut impl FnMut(&ErasedExpr), mode: VisitTableMode) {
$(self.$idx.visit(f, mode);)*
}
fn visit_mut(&mut self, f: &mut impl FnMut(&mut ErasedExpr), mode: VisitTableMode) {
$(self.$idx.visit_mut(f, mode);)*
}
}
$(#[$meta])*
impl<$($ty,)*> ShortenLifetime for ($($ty,)*)
where $($ty: ShortenLifetime,)*
{
type Shortened<'small>
= ($($ty::Shortened<'small>,)*)
where
Self: 'small;
fn shorten_lifetime<'small, 'large: 'small>(self) -> Self::Shortened<'small>
where
Self: 'large,
{
($(self.$idx.shorten_lifetime(),)*)
}
}
$(#[$meta])*
impl<$($ty,)*> ForLifetimeTable for ($($ty,)*)
where $($ty: ForLifetimeTable,)*
{
type WithLt<'lt>
= ($($ty::WithLt<'lt>,)*);
fn with_lt<'lt>(self, marker: &mut WithLtMarker) -> Self::WithLt<'lt> {
($(self.$idx.with_lt(marker),)*)
}
}
#[cfg(feature = "sqlx")]
$(#[$meta])*
impl<$($ty,)*> TableLoaderSqlx for ($($ty,)*)
where $($ty: TableLoaderSqlx,)*
{
fn load<'a>(
&self,
values: &mut impl Iterator<Item = sqlx::postgres::PgValueRef<'a>>,
) -> Self::Result {
(
$(self.$idx.load(values),)*
)
}
fn skip<'a>(&self, values: &mut impl Iterator<Item = sqlx::postgres::PgValueRef<'a>>) {
$(self.$idx.skip(values);)*
}
}
#[cfg(feature = "sqlx")]
$(#[$meta])*
impl<$($ty,)*> TableLoaderManySqlx for ($($ty,)*)
where $($ty: TableLoaderManySqlx,)*
{
fn load_many<'a>(
&self,
values: &mut impl Iterator<Item = sqlx::postgres::PgValueRef<'a>>,
) -> Vec<Self::Result> {
izip_priv!($(self.$idx.load_many(values),)*).collect()
}
}
}
}
variadics_please::all_tuples_enumerated!(
#[doc(fake_variadic)]
impl_tuples,
1,
15,
T
);
pub struct MaybeTable<'scope, T> {
tag: Expr<'scope, Option<bool>>,
inner: T,
}
impl<'scope, T: Table<'scope>> MaybeTable<'scope, T> {
pub fn project<U, F>(&self, f: F) -> Expr<'scope, Option<U>>
where
F: FnOnce(&T) -> Expr<U>,
{
let tag = self.tag.clone();
let e = f(&self.inner);
let cb = Arc::new(|cond: sea_query::SimpleExpr, expr: sea_query::SimpleExpr| {
sea_query::CaseStatement::new()
.case(cond.is_not_null(), expr)
.finally(sea_query::Expr::null())
.into()
});
Expr::new(ExprInner::BinOp(cb, Box::new(tag.expr), Box::new(e.expr)))
}
pub fn maybe<U: Table<'scope>, F>(self, fallback: U, f: F) -> U
where
F: FnOnce(T) -> U,
{
let tag = self.tag.clone();
let mut e = f(self.inner);
let mut defaults = collect_exprs(&fallback).into_iter();
e.visit_mut(
&mut |ErasedExpr(inner)| {
let tag = tag.expr.clone();
let default = defaults.next().unwrap();
let cb = Arc::new(|exprs: Vec<sea_query::Expr>| {
let [tag, expr, default] = exprs.try_into().unwrap();
sea_query::CaseStatement::new()
.case(tag.is_not_null(), expr)
.finally(default)
.into()
});
*inner = ExprInner::NOp(cb, vec![tag, inner.clone(), default]);
},
VisitTableMode::All,
);
e
}
}
impl<'scope, T: Table<'scope>> Table<'scope> for MaybeTable<'scope, T> {
type Result = Option<T::Result>;
fn visit(&self, f: &mut impl FnMut(&ErasedExpr), mode: VisitTableMode) {
self.tag.visit(f, mode);
self.inner.visit(f, mode);
}
fn visit_mut(&mut self, f: &mut impl FnMut(&mut ErasedExpr), mode: VisitTableMode) {
self.tag.visit_mut(f, mode);
self.inner.visit_mut(f, mode);
}
}
impl<'scope, T: ForLifetimeTable + Table<'scope>> ForLifetimeTable for MaybeTable<'scope, T>
where
for<'lt> T::WithLt<'lt>: Table<'lt>,
{
type WithLt<'lt> = MaybeTable<'lt, T::WithLt<'lt>>;
fn with_lt<'lt>(self, marker: &mut WithLtMarker) -> Self::WithLt<'lt> {
MaybeTable {
tag: self.tag.with_lt(marker),
inner: self.inner.with_lt(marker),
}
}
}
impl<'scope, T: ShortenLifetime> ShortenLifetime for MaybeTable<'scope, T> {
type Shortened<'small>
= MaybeTable<'small, T::Shortened<'small>>
where
Self: 'small;
fn shorten_lifetime<'small, 'large: 'small>(self) -> Self::Shortened<'small>
where
Self: 'large,
{
MaybeTable {
tag: self.tag.shorten_lifetime(),
inner: self.inner.shorten_lifetime(),
}
}
}
#[cfg(feature = "sqlx")]
impl<T: TableLoaderSqlx> TableLoaderSqlx for MaybeTable<'static, T> {
fn load<'a>(
&self,
values: &mut impl Iterator<Item = sqlx::postgres::PgValueRef<'a>>,
) -> Self::Result {
let tag =
<Option<bool> as sqlx::Decode<sqlx::Postgres>>::decode(values.next().unwrap()).unwrap();
if tag == Some(true) {
Some(self.inner.load(values))
} else {
self.inner.skip(values);
None
}
}
fn skip<'a>(&self, values: &mut impl Iterator<Item = sqlx::postgres::PgValueRef<'a>>) {
values.next().unwrap();
self.inner.skip(values);
}
}
pub struct NullTable<'scope, T> {
tag: Expr<'scope, Option<bool>>,
inner: T,
}
impl<'scope, T: Table<'scope>> NullTable<'scope, T> {
pub fn project<U, F>(&self, f: F) -> Expr<'scope, Option<U>>
where
F: FnOnce(&T) -> Expr<U>,
{
let e = f(&self.inner);
Expr::new(e.expr)
}
pub fn maybe<U: Table<'scope>, F>(self, fallback: U, f: F) -> U
where
F: FnOnce(T) -> U,
{
let tag = self.tag.clone();
let mut e = f(self.inner);
let mut defaults = collect_exprs(&fallback).into_iter();
e.visit_mut(
&mut |ErasedExpr(inner)| {
let tag = tag.expr.clone();
let default = defaults.next().unwrap();
let cb = Arc::new(|exprs: Vec<sea_query::Expr>| {
let [tag, expr, default] = exprs.try_into().unwrap();
sea_query::CaseStatement::new()
.case(tag.is_not_null(), expr)
.finally(default)
.into()
});
*inner = ExprInner::NOp(cb, vec![tag, inner.clone(), default]);
},
VisitTableMode::All,
);
e
}
}
impl<'scope, T: Table<'scope>> Table<'scope> for NullTable<'scope, T> {
type Result = Option<T::Result>;
fn visit(&self, f: &mut impl FnMut(&ErasedExpr), mode: VisitTableMode) {
self.tag.visit(f, mode);
self.inner.visit(f, mode);
}
fn visit_mut(&mut self, f: &mut impl FnMut(&mut ErasedExpr), mode: VisitTableMode) {
self.tag.visit_mut(f, mode);
self.inner.visit_mut(f, mode);
}
}
impl<'scope, T: ForLifetimeTable + Table<'scope>> ForLifetimeTable for NullTable<'scope, T>
where
for<'lt> T::WithLt<'lt>: Table<'lt>,
{
type WithLt<'lt> = NullTable<'lt, T::WithLt<'lt>>;
fn with_lt<'lt>(self, marker: &mut WithLtMarker) -> Self::WithLt<'lt> {
NullTable {
tag: self.tag.with_lt(marker),
inner: self.inner.with_lt(marker),
}
}
}
impl<'scope, T: ShortenLifetime> ShortenLifetime for NullTable<'scope, T> {
type Shortened<'small>
= NullTable<'small, T::Shortened<'small>>
where
Self: 'small;
fn shorten_lifetime<'small, 'large: 'small>(self) -> Self::Shortened<'small>
where
Self: 'large,
{
NullTable {
tag: self.tag.shorten_lifetime(),
inner: self.inner.shorten_lifetime(),
}
}
}
#[cfg(feature = "sqlx")]
impl<T: TableLoaderSqlx> TableLoaderSqlx for NullTable<'static, T> {
fn load<'a>(
&self,
values: &mut impl Iterator<Item = sqlx::postgres::PgValueRef<'a>>,
) -> Self::Result {
let tag =
<Option<bool> as sqlx::Decode<sqlx::Postgres>>::decode(values.next().unwrap()).unwrap();
if tag == Some(true) {
Some(self.inner.load(values))
} else {
self.inner.skip(values);
None
}
}
fn skip<'a>(&self, values: &mut impl Iterator<Item = sqlx::postgres::PgValueRef<'a>>) {
values.next().unwrap();
self.inner.skip(values);
}
}
pub struct ListTable<T> {
inner: T,
}
impl<'scope, T: Table<'scope>> Table<'scope> for ListTable<T> {
type Result = Vec<T::Result>;
fn visit(&self, f: &mut impl FnMut(&ErasedExpr), mode: VisitTableMode) {
self.inner.visit(f, mode);
}
fn visit_mut(&mut self, f: &mut impl FnMut(&mut ErasedExpr), mode: VisitTableMode) {
self.inner.visit_mut(f, mode);
}
}
impl<'scope, T: ForLifetimeTable + Table<'scope>> ForLifetimeTable for ListTable<T>
where
for<'lt> T::WithLt<'lt>: Table<'lt>,
{
type WithLt<'lt> = ListTable<T::WithLt<'lt>>;
fn with_lt<'lt>(self, marker: &mut WithLtMarker) -> Self::WithLt<'lt> {
ListTable {
inner: self.inner.with_lt(marker),
}
}
}
impl<T: ShortenLifetime> ShortenLifetime for ListTable<T> {
type Shortened<'small>
= ListTable<T::Shortened<'small>>
where
Self: 'small;
fn shorten_lifetime<'small, 'large: 'small>(self) -> Self::Shortened<'small>
where
Self: 'large,
{
ListTable {
inner: self.inner.shorten_lifetime(),
}
}
}
#[cfg(feature = "sqlx")]
impl<T: TableLoaderManySqlx> TableLoaderSqlx for ListTable<T> {
fn load<'a>(
&self,
values: &mut impl Iterator<Item = sqlx::postgres::PgValueRef<'a>>,
) -> Self::Result {
self.inner.load_many(values)
}
fn skip<'a>(&self, values: &mut impl Iterator<Item = sqlx::postgres::PgValueRef<'a>>) {
self.inner.skip(values);
}
}
#[derive(Clone)]
pub struct Query<T> {
binder: Binder,
expr: sea_query::SelectStatement,
inner: T,
siblings_need_random: bool,
}
impl<'scope, T> Query<T>
where
T: Table<'scope> + ForLifetimeTable,
{
fn new(binder: Binder, expr: sea_query::SelectStatement, inner: T) -> Self {
Self {
binder,
expr,
inner,
siblings_need_random: false,
}
}
fn into_volatile(mut self) -> Self {
self.siblings_need_random = true;
self
}
fn with_volatile(mut self, volatility: bool) -> Self {
self.siblings_need_random = volatility;
self
}
fn erased(self) -> (ErasedQuery, T) {
let r = ErasedQuery {
expr: self.expr,
siblings_need_random: self.siblings_need_random,
};
(r, self.inner)
}
pub fn aggregate<U>(
self,
f: impl for<'inner, 'outer> FnOnce(
&mut A<'inner, 'outer>,
T::WithLt<'inner>,
) -> U::WithLt<'outer>,
) -> Query<U::WithLt<'scope>>
where
U: ForLifetimeTable,
{
let mut a = A {
group_by: vec![],
_phantom: PhantomData,
};
let mut r = f(&mut a, self.inner.with_lt(&mut WithLtMarker::new()));
let binder = Binder::new();
let mut expr = sea_query::SelectStatement::new();
expr.from_subquery(self.expr, TableName::new(self.binder));
subst_table(&mut r, TableName::new(binder), &mut expr);
expr.add_group_by(a.group_by.iter().map(ExprInner::render));
Query::new(binder, expr, r)
}
pub fn many(mut self) -> Query<ListTable<T>> {
let binder = Binder::new();
let mut expr = sea_query::SelectStatement::new();
expr.from_subquery(self.expr, TableName::new(self.binder));
self.inner.visit_mut(
&mut |ErasedExpr(inner)| {
*inner = ExprInner::NOp(
Arc::new(|inners| {
let [inner] = inners.try_into().unwrap();
sea_query::PgFunc::array_agg(inner).into()
}),
vec![inner.clone()],
)
},
VisitTableMode::All,
);
subst_table(&mut self.inner, TableName::new(binder), &mut expr);
Query::new(binder, expr, ListTable { inner: self.inner })
.with_volatile(self.siblings_need_random)
}
pub fn window<U>(
self,
f: impl for<'inner, 'outer> FnOnce(
&mut W<'inner, 'outer>,
T::WithLt<'inner>,
) -> U::WithLt<'outer>,
) -> Query<U::WithLt<'scope>>
where
U: ForLifetimeTable + Table<'scope>,
{
let inner_binder = Binder::new();
let middle_binder = Binder::new();
let outer_binder = Binder::new();
let mut w = W {
inner_query: sea_query::SelectStatement::new(),
inner_table: TableName::new(inner_binder),
middle_query: sea_query::SelectStatement::new(),
middle_table: TableName::new(middle_binder),
_phantom: PhantomData,
};
let mut r = f(&mut w, self.inner.with_lt(&mut WithLtMarker::new()));
w.inner_query
.from_subquery(self.expr, TableName::new(self.binder));
w.middle_query.from_subquery(w.inner_query, w.inner_table);
let mut outer_query = sea_query::SelectStatement::new();
outer_query.from_subquery(w.middle_query, w.middle_table.clone());
subst_table(&mut r, w.middle_table, &mut outer_query);
Query::new(outer_binder, outer_query, r)
}
pub fn evaluate(mut table: T) -> Self {
let binder = Binder::new();
let mut expr = sea_query::SelectStatement::new();
subst_table(&mut table, TableName::new(binder), &mut expr);
Self::new(binder, expr, table).into_volatile()
}
pub fn optional(mut self) -> Query<MaybeTable<'scope, T>> {
let binder = Binder::new();
let mut filler = sea_query::Query::select()
.from_values(
vec![sea_query::ValueTuple::One(true.into())],
TableName::new(binder),
)
.to_owned();
let tag = ColumnName::new(binder, "tag".to_owned());
let mut expr = self.expr;
expr.expr_as(sea_query::Value::Bool(Some(true)), tag.clone());
let table_name = TableName::new(self.binder);
filler.join_subquery(
sea_query::JoinType::LeftJoin,
expr,
table_name.clone(),
sea_query::Condition::all(),
);
filler.expr_as(
sea_query::Expr::column((table_name.clone(), tag.clone())),
tag.clone(),
);
subst_table(&mut self.inner, table_name, &mut filler);
let maybe_table = MaybeTable {
inner: self.inner,
tag: Expr {
expr: ExprInner::Column(TableName::new(binder), tag),
_phantom: PhantomData,
},
};
Query {
binder,
expr: filler,
inner: maybe_table,
siblings_need_random: self.siblings_need_random,
}
}
pub fn nullable(mut self) -> Query<NullTable<'scope, T>> {
let binder = Binder::new();
let tag = ColumnName::new(binder, "tag".to_owned());
let mut non_null_exprs = vec![];
self.inner.visit(
&mut |ErasedExpr(e)| {
non_null_exprs.push(e.render().is_not_null());
},
VisitTableMode::NonNull,
);
let non_null_expr = non_null_exprs
.into_iter()
.fold(sea_query::Condition::all(), |a, b| a.add(b));
let mut expr = self.expr;
expr.expr_as(non_null_expr, tag.clone());
let table_name = TableName::new(self.binder);
let mut query = sea_query::Query::select();
query.from_subquery(expr, table_name.clone());
query.expr_as(
sea_query::Expr::column((table_name.clone(), tag.clone())),
tag.clone(),
);
subst_table(&mut self.inner, table_name, &mut query);
let null_table = NullTable {
inner: self.inner,
tag: Expr::new(ExprInner::Column(TableName::new(binder), tag)),
};
Query {
binder,
expr: query,
inner: null_table,
siblings_need_random: self.siblings_need_random,
}
}
pub fn order_by<U, F>(self, f: F) -> Query<T>
where
U: Table<'scope>,
F: FnOnce(&T) -> (U, sea_query::Order),
{
let binder = Binder::new();
let mut outer = sea_query::Query::select();
outer.from_subquery(self.expr, TableName::new(self.binder));
let (order_expr, order) = f(&self.inner);
order_expr.visit(
&mut |ErasedExpr(e)| {
outer.order_by_expr(e.render(), order.clone());
},
VisitTableMode::All,
);
let mut e = self.inner;
subst_table(&mut e, TableName::new(binder), &mut outer);
Query {
binder,
expr: outer,
inner: e,
siblings_need_random: self.siblings_need_random,
}
}
}
impl<'scope, T> Query<T>
where
T: MapTable<'scope> + TableHKT<Mode = NameMode>,
T::InMode<ExprMode>: ForLifetimeTable + Table<'scope>,
{
pub fn each(schema: &TableSchema<T>) -> Query<T::InMode<ExprMode>> {
let binder = Binder::new();
let mut query = sea_query::Query::select();
query.from(schema.name);
let mut mapper = NameToExprMapper { binder, query };
let expr = schema.columns.map_modes_ref(&mut mapper);
Query::new(binder, mapper.query, expr)
}
}
impl<'scope, T: TableHKT<Mode = ValueMode>> Query<T> {
pub fn values(vals: impl IntoIterator<Item = T>) -> Query<T::InMode<ExprMode>>
where
T: MapTable<'scope>,
T::InMode<ExprMode>: ForLifetimeTable + Table<'scope>,
{
let binder = Binder::new();
let mut iter = vals.into_iter();
let Some(first) = iter.next() else {
panic!("Don't do that");
};
let mut mapper = ExprCollectorMapper {
idx: 0,
table_binder: binder,
columns: Vec::new(),
values: Vec::new(),
};
let result_expr = first.map_modes(&mut mapper);
let mut all_values = vec![mapper.values];
for v in iter {
let mut mapper = ExprCollectorRemainingMapper { values: Vec::new() };
v.map_modes(&mut mapper);
all_values.push(mapper.values);
}
let mut select = sea_query::Query::select();
for (idx, col) in mapper.columns.into_iter().enumerate() {
select.expr_as(sea_query::Expr::column(format!("column{}", idx + 1)), col);
}
select.from_values(
all_values
.into_iter()
.map(|v| sea_query::ValueTuple::Many(v)),
TableName::new(binder),
);
Query::new(binder, select, result_expr)
}
}
#[cfg(feature = "sqlx")]
impl<T: TableLoaderSqlx> Query<T> {
pub async fn all(&self, pool: &mut sqlx::PgConnection) -> sqlx::Result<Vec<T::Result>> {
use sea_query::PostgresQueryBuilder;
use sea_query_sqlx::SqlxBinder as _;
use sqlx::Row as _;
let (sql, values) = self.expr.build_sqlx(PostgresQueryBuilder);
let all = sqlx::query_with(&sql, values).fetch_all(pool).await?;
Ok(all
.into_iter()
.map(|row| {
let len = row.len();
let mut it = (0..len).map(|x| row.try_get_raw(x).unwrap());
self.inner.load(&mut it)
})
.collect::<Vec<_>>())
}
}
#[derive(Debug)]
struct ErasedQuery {
expr: sea_query::SelectStatement,
siblings_need_random: bool,
}
#[derive(bytemuck::TransparentWrapper)]
#[repr(transparent)]
pub struct ErasedExpr(ExprInner);
#[derive(Clone)]
enum ExprInner {
Raw(sea_query::Expr),
Column(TableName, ColumnName),
BinOp(
Arc<dyn Fn(sea_query::SimpleExpr, sea_query::SimpleExpr) -> sea_query::SimpleExpr>,
Box<ExprInner>,
Box<ExprInner>,
),
NOp(
Arc<dyn Fn(Vec<sea_query::SimpleExpr>) -> sea_query::SimpleExpr>,
Vec<ExprInner>,
),
}
impl ExprInner {
fn visit_mut(&mut self, f: &mut impl FnMut(&mut TableName, &mut ColumnName)) {
match self {
ExprInner::Column(table_name, column_name) => f(table_name, column_name),
ExprInner::BinOp(_, expr_inner, expr_inner1) => {
expr_inner.visit_mut(f);
expr_inner1.visit_mut(f);
}
ExprInner::NOp(_, inners) => {
for inner in inners {
inner.visit_mut(f);
}
}
ExprInner::Raw(_) => {}
}
}
fn render(&self) -> sea_query::SimpleExpr {
match self {
ExprInner::Raw(value) => value.clone(),
ExprInner::Column(table_name, column_name) => {
sea_query::Expr::column((table_name.clone(), column_name.clone()))
}
ExprInner::BinOp(cb, expr_inner, expr_inner1) => {
cb(expr_inner.render(), expr_inner1.render())
}
ExprInner::NOp(cb, inners) => cb(inners.into_iter().map(|n| n.render()).collect()),
}
}
}
#[derive(Clone)]
pub struct Expr<'scope, T> {
expr: ExprInner,
_phantom: PhantomData<(&'scope (), T)>,
}
impl<'scope, T> Expr<'scope, T> {
fn new(expr: ExprInner) -> Self {
Self {
expr,
_phantom: PhantomData,
}
}
pub fn lit(value: T) -> Self
where
T: Into<sea_query::Value>,
{
Self::new(ExprInner::Raw(sea_query::Expr::value(value.into())))
}
fn binop<U>(
self,
other: Self,
binop: Arc<dyn Fn(sea_query::SimpleExpr, sea_query::SimpleExpr) -> sea_query::SimpleExpr>,
) -> Expr<'scope, U> {
Expr::new(ExprInner::BinOp(
binop,
Box::new(self.expr),
Box::new(other.expr),
))
}
pub fn equals(self, other: Self) -> Expr<'scope, bool> {
self.binop(
other,
Arc::new(|a, b| a.binary(sea_query::BinOper::Equal, b)),
)
}
fn as_erased(&self) -> &ErasedExpr {
ErasedExpr::wrap_ref(&self.expr)
}
fn as_erased_mut(&mut self) -> &mut ErasedExpr {
ErasedExpr::wrap_mut(&mut self.expr)
}
}
impl<'scope> Expr<'scope, i32> {
pub fn add(self, other: Self) -> Self {
self.binop(other, Arc::new(|a, b| a.binary(sea_query::BinOper::Add, b)))
}
pub fn nextval(name: &str) -> Self {
Self::new(ExprInner::Raw(
sea_query::Func::cust("nextval").arg(name.to_owned()).into(),
))
}
}
pub struct Q<'scope> {
queries: Vec<(TableName, ErasedQuery)>,
filters: Vec<ExprInner>,
binder: Binder,
_phantom: PhantomData<&'scope ()>,
}
impl<'scope> Q<'scope> {
pub fn q<T: ForLifetimeTable + Table<'scope>>(&mut self, query: Query<T>) -> T {
let binder = Binder::new();
let name = TableName::new(binder);
let (erased, mut inner) = query.erased();
self.queries.push((name.clone(), erased));
insert_table_name(&mut inner, name);
inner
}
pub fn where_<'a>(&mut self, expr: Expr<'a, bool>)
where
'scope: 'a,
{
self.filters.push(expr.expr);
}
}
pub fn query<'outer, T: ForLifetimeTable>(
f: impl for<'scope> FnOnce(&mut Q<'scope>) -> T::WithLt<'scope>,
) -> Query<T::WithLt<'outer>> {
let mut q = Q {
binder: Binder::new(),
filters: Vec::new(),
queries: Vec::new(),
_phantom: PhantomData,
};
let mut e = f(&mut q);
let needs_random = q.queries.iter().any(|(_, q)| q.siblings_need_random);
let mut random_binders: Vec<sea_query::Expr> = Vec::new();
let mut insert_dummy = |mut stmt: sea_query::SelectStatement, table: &TableName| {
if needs_random {
stmt.expr_as(sea_query::Func::random(), "dummy");
for binder in &random_binders {
stmt.and_where(binder.clone());
}
random_binders
.push(sea_query::Expr::column((table.clone(), "dummy".to_string())).is_not_null())
}
stmt
};
let mut iter = q.queries.into_iter();
let mut table = sea_query::Query::select();
if let Some((first_table_name, first)) = iter.next() {
let expr = insert_dummy(first.expr, &first_table_name);
table.from_subquery(expr, first_table_name);
};
for (table_name, q) in iter {
let expr = insert_dummy(q.expr, &table_name);
table.join_lateral(
sea_query::JoinType::InnerJoin,
expr,
table_name,
sea_query::Condition::all(),
);
}
for filter in q.filters {
table.and_where(filter.render());
}
subst_table(&mut e, TableName::new(q.binder), &mut table);
let mut q = Query::new(q.binder, table.to_owned(), e);
q.siblings_need_random = needs_random;
q
}
pub struct Insert<T: TableHKT<Mode = NameMode>> {
pub into: TableSchema<T>,
pub rows: Query<T::InMode<ExprMode>>,
}
#[cfg(feature = "sqlx")]
impl<T: TableHKT<Mode = NameMode>> Insert<T>
where
T: MapTable<'static>,
{
pub async fn run(
&self,
pool: &mut sqlx::PgConnection,
) -> sqlx::Result<sqlx::postgres::PgQueryResult> {
use sea_query::PostgresQueryBuilder;
use sea_query_sqlx::SqlxBinder as _;
let mut insert = sea_query::Query::insert()
.into_table(self.into.name)
.to_owned();
let mut mapper = NameCollectorMapper { names: Vec::new() };
self.into.columns.map_modes_ref(&mut mapper);
insert.columns(mapper.names);
insert.select_from(self.rows.expr.clone()).unwrap();
let (sql, values) = insert.build_sqlx(PostgresQueryBuilder);
let all = sqlx::query_with(&sql, values).execute(pool).await?;
Ok(all)
}
}
pub struct A<'inner, 'outer> {
group_by: Vec<ExprInner>,
_phantom: PhantomData<(&'inner (), &'outer ())>,
}
impl<'inner, 'outer> A<'inner, 'outer> {
pub fn group_by<T: Table<'inner> + ForLifetimeTable>(&mut self, expr: T) -> T::WithLt<'outer> {
expr.visit(
&mut |ErasedExpr(e)| {
self.group_by.push(e.clone());
},
VisitTableMode::All,
);
expr.with_lt(&mut WithLtMarker::new())
}
pub fn array_agg<T: Table<'inner> + ForLifetimeTable>(
&mut self,
mut expr: T,
) -> ListTable<T::WithLt<'outer>> {
expr.visit_mut(
&mut |ErasedExpr(inner)| {
*inner = ExprInner::NOp(
Arc::new(|inners| {
let [inner] = inners.try_into().unwrap();
sea_query::PgFunc::array_agg(inner).into()
}),
vec![inner.clone()],
)
},
VisitTableMode::All,
);
ListTable {
inner: expr.with_lt(&mut WithLtMarker::new()),
}
}
fn simple_agg_fn<T: Table<'inner> + ForLifetimeTable>(
&mut self,
mut expr: T,
f: impl Fn(sea_query::Expr) -> sea_query::Expr + Clone + 'static,
) -> T::WithLt<'outer> {
expr.visit_mut(
&mut |ErasedExpr(inner)| {
let f = f.clone();
*inner = ExprInner::NOp(
Arc::new(move |inners| {
let [inner] = inners.try_into().unwrap();
f(inner)
}),
vec![inner.clone()],
)
},
VisitTableMode::All,
);
expr.with_lt(&mut WithLtMarker::new())
}
pub fn avg<T: Table<'inner> + ForLifetimeTable>(&mut self, expr: T) -> T::WithLt<'outer> {
self.simple_agg_fn(expr, |e| e.avg())
}
pub fn bit_and<T: Table<'inner> + ForLifetimeTable>(&mut self, expr: T) -> T::WithLt<'outer> {
self.simple_agg_fn(expr, |e| sea_query::Func::bit_and(e).into())
}
pub fn bit_or<T: Table<'inner> + ForLifetimeTable>(&mut self, expr: T) -> T::WithLt<'outer> {
self.simple_agg_fn(expr, |e| sea_query::Func::bit_or(e).into())
}
pub fn bit_xor<T: Table<'inner> + ForLifetimeTable>(&mut self, expr: T) -> T::WithLt<'outer> {
self.simple_agg_fn(expr, |e| sea_query::Func::cust("BIT_XOR").arg(e).into())
}
pub fn bool_and<T: Table<'inner> + ForLifetimeTable>(&mut self, expr: T) -> T::WithLt<'outer> {
self.simple_agg_fn(expr, |e| sea_query::Func::cust("BOOL_AND").arg(e).into())
}
pub fn bool_or<T: Table<'inner> + ForLifetimeTable>(&mut self, expr: T) -> T::WithLt<'outer> {
self.simple_agg_fn(expr, |e| sea_query::Func::cust("BOOL_OR").arg(e).into())
}
pub fn count_star(&mut self) -> Expr<'outer, i32> {
Expr::new(ExprInner::Raw(sea_query::Expr::cust("*").count()))
}
pub fn count<T>(&mut self, expr: Expr<'inner, T>) -> Expr<'outer, i32> {
Expr::new(ExprInner::NOp(
Arc::new(|inners| {
let [inner] = inners.try_into().unwrap();
inner.count()
}),
vec![expr.expr],
))
}
pub fn count_distinct<T>(&mut self, expr: Expr<'inner, T>) -> Expr<'outer, i32> {
Expr::new(ExprInner::NOp(
Arc::new(|inners| {
let [inner] = inners.try_into().unwrap();
inner.count_distinct()
}),
vec![expr.expr],
))
}
pub fn max<T: Table<'inner> + ForLifetimeTable>(&mut self, expr: T) -> T::WithLt<'outer> {
self.simple_agg_fn(expr, |e| e.max())
}
pub fn min<T: Table<'inner> + ForLifetimeTable>(&mut self, expr: T) -> T::WithLt<'outer> {
self.simple_agg_fn(expr, |e| e.min())
}
pub fn sum<T: Table<'inner> + ForLifetimeTable>(&mut self, expr: T) -> T::WithLt<'outer> {
self.simple_agg_fn(expr, |e| e.sum())
}
}
#[derive(Clone)]
pub struct Over<'scope> {
partitions: Vec<ExprInner>,
orderings: Vec<(ExprInner, sea_query::Order)>,
_phantom: PhantomData<&'scope ()>,
}
impl<'scope> Over<'scope> {
pub fn new() -> Self {
Self {
partitions: Vec::new(),
orderings: Vec::new(),
_phantom: PhantomData,
}
}
pub fn order_by<T: Table<'scope>>(mut self, expr: T, order: sea_query::Order) -> Self {
expr.visit(
&mut |ErasedExpr(e)| self.orderings.push((e.clone(), order.clone())),
VisitTableMode::All,
);
self
}
pub fn partition_by<T: Table<'scope>>(mut self, expr: T) -> Self {
expr.visit(
&mut |ErasedExpr(e)| self.partitions.push(e.clone()),
VisitTableMode::All,
);
self
}
}
pub struct W<'inner, 'outer> {
inner_query: sea_query::SelectStatement,
inner_table: TableName,
middle_query: sea_query::SelectStatement,
middle_table: TableName,
_phantom: PhantomData<(&'inner (), &'outer ())>,
}
impl<'inner, 'outer> W<'inner, 'outer> {
fn generic_window(&mut self, expr: sea_query::Expr, over: Over<'inner>) -> ExprInner {
let mut window_statement = sea_query::WindowStatement::new();
for expr in over.partitions {
let name = ColumnName::new(Binder::new(), "part".to_owned());
self.inner_query.expr_as(expr.render(), name.clone());
window_statement.partition_by((self.inner_table.clone(), name));
}
for (expr, order) in over.orderings {
let name = ColumnName::new(Binder::new(), "ord".to_owned());
self.inner_query.expr_as(expr.render(), name.clone());
window_statement.order_by((self.inner_table.clone(), name), order);
}
let column = ColumnName::new(Binder::new(), "win".to_owned());
self.middle_query
.expr_window_as(expr, window_statement, column.clone());
ExprInner::Column(self.middle_table.clone(), column)
}
pub fn row_number(&mut self, over: Over<'inner>) -> Expr<'outer, i64> {
Expr::new(self.generic_window(sea_query::Func::cust("row_number").into(), over))
}
pub fn rank(&mut self, over: Over<'inner>) -> Expr<'outer, i64> {
Expr::new(self.generic_window(sea_query::Func::cust("rank").into(), over))
}
pub fn dense_rank(&mut self, over: Over<'inner>) -> Expr<'outer, i64> {
Expr::new(self.generic_window(sea_query::Func::cust("dense_rank").into(), over))
}
pub fn percent_rank(&mut self, over: Over<'inner>) -> Expr<'outer, f64> {
Expr::new(self.generic_window(sea_query::Func::cust("percent_rank").into(), over))
}
pub fn cume_dist(&mut self, over: Over<'inner>) -> Expr<'outer, f64> {
Expr::new(self.generic_window(sea_query::Func::cust("cume_dist").into(), over))
}
pub fn ntile(
&mut self,
num_buckets: Expr<'outer, i64>,
over: Over<'inner>,
) -> Expr<'outer, f64> {
Expr::new(
self.generic_window(
sea_query::Func::cust("ntlile")
.arg(num_buckets.expr.render())
.into(),
over,
),
)
}
fn lag_lead_generic<T: Table<'inner> + ForLifetimeTable>(
&mut self,
fn_: &'static str,
mut value: T,
offset: Option<Expr<'outer, i64>>,
default: Option<T>,
over: Over<'inner>,
) -> T::WithLt<'outer> {
let mut default_exprs = default.as_ref().map(collect_exprs).map(|x| x.into_iter());
let mut window_statement = sea_query::WindowStatement::new();
for expr in over.partitions {
let name = ColumnName::new(Binder::new(), "part".to_owned());
self.inner_query.expr_as(expr.render(), name.clone());
window_statement.partition_by((self.inner_table.clone(), name));
}
for (expr, order) in over.orderings {
let name = ColumnName::new(Binder::new(), "ord".to_owned());
self.inner_query.expr_as(expr.render(), name.clone());
window_statement.order_by((self.inner_table.clone(), name), order);
}
subst_table(&mut value, self.inner_table.clone(), &mut self.inner_query);
value.visit_mut(
&mut |ErasedExpr(inner)| {
let offset = offset.clone();
let default = default_exprs.as_mut().map(|x| x.next().unwrap());
let r = sea_query::Func::cust(fn_).arg(inner.clone().render());
let (r, set_default) = if let Some(offset) = offset {
(r.arg(offset.expr.render()), true)
} else {
(r, false)
};
let r = if let Some(default) = default {
let r = if !set_default {
r.arg(sea_query::Expr::value(1))
} else {
r
};
r.arg(default.render())
} else {
r
};
let column = ColumnName::new(Binder::new(), "win".to_owned());
self.middle_query
.expr_window_as(r, window_statement.clone(), column.clone());
*inner = ExprInner::Column(self.middle_table.clone(), column);
},
VisitTableMode::All,
);
value.with_lt(&mut WithLtMarker::new())
}
pub fn lag<T: Table<'inner> + ForLifetimeTable>(
&mut self,
value: T,
offset: Option<Expr<'outer, i64>>,
default: Option<T>,
over: Over<'inner>,
) -> T::WithLt<'outer> {
self.lag_lead_generic("lag", value, offset, default, over)
}
pub fn lead<T: Table<'inner> + ForLifetimeTable>(
&mut self,
value: T,
offset: Option<Expr<'outer, i64>>,
default: Option<T>,
over: Over<'inner>,
) -> T::WithLt<'outer> {
self.lag_lead_generic("lead", value, offset, default, over)
}
pub fn first_value<T: Table<'inner> + ForLifetimeTable>(
&mut self,
value: T,
over: Over<'inner>,
) -> T::WithLt<'outer> {
self.lag_lead_generic("first_value", value, None, None, over)
}
pub fn last_value<T: Table<'inner> + ForLifetimeTable>(
&mut self,
value: T,
over: Over<'inner>,
) -> T::WithLt<'outer> {
self.lag_lead_generic("first_value", value, None, None, over)
}
pub fn id<T: Table<'inner> + ForLifetimeTable>(&mut self, mut table: T) -> T::WithLt<'outer> {
subst_table(&mut table, self.inner_table.clone(), &mut self.inner_query);
subst_table(
&mut table,
self.middle_table.clone(),
&mut self.middle_query,
);
table.with_lt(&mut WithLtMarker::new())
}
}
pub mod helper_tables {
use super::*;
use rust_rel8_derive::TableStruct;
#[derive(TableStruct)]
#[table(crate = "crate")]
#[perfect_derive::perfect_derive(Debug, PartialEq, Clone)]
pub struct One<'scope, Mode: TableMode, #[table(proxy)] A: Value> {
pub a: Mode::T<'scope, A>,
}
#[derive(TableStruct)]
#[table(crate = "crate")]
#[perfect_derive::perfect_derive(Debug, PartialEq, Clone)]
pub struct Two<'scope, Mode: TableMode, #[table(proxy)] A: Value, #[table(proxy)] B: Value> {
pub a: Mode::T<'scope, A>,
pub b: Mode::T<'scope, B>,
}
#[derive(TableStruct)]
#[table(crate = "crate")]
#[perfect_derive::perfect_derive(Debug, PartialEq, Clone)]
pub struct Three<
'scope,
Mode: TableMode,
#[table(proxy)] A: Value,
#[table(proxy)] B: Value,
#[table(proxy)] C: Value,
> {
pub a: Mode::T<'scope, A>,
pub b: Mode::T<'scope, B>,
pub c: Mode::T<'scope, C>,
}
}
pub mod helper_utilities {
use std::{collections::HashMap, hash::Hash};
pub trait ShortenLifetime {
type Shortened<'small>
where
Self: 'small;
fn shorten_lifetime<'small, 'large: 'small>(self) -> Self::Shortened<'small>
where
Self: 'large;
}
impl<T: ShortenLifetime, const N: usize> ShortenLifetime for [T; N] {
type Shortened<'small>
= [T::Shortened<'small>; N]
where
Self: 'small;
fn shorten_lifetime<'small, 'large: 'small>(self) -> Self::Shortened<'small>
where
Self: 'large,
{
self.map(ShortenLifetime::shorten_lifetime)
}
}
impl<T: ShortenLifetime> ShortenLifetime for Vec<T> {
type Shortened<'small>
= Vec<T::Shortened<'small>>
where
Self: 'small;
fn shorten_lifetime<'small, 'large: 'small>(self) -> Self::Shortened<'small>
where
Self: 'large,
{
self.into_iter()
.map(ShortenLifetime::shorten_lifetime)
.collect::<Vec<_>>()
}
}
impl<K: Hash + Eq, T: ShortenLifetime> ShortenLifetime for HashMap<K, T> {
type Shortened<'small>
= HashMap<K, T::Shortened<'small>>
where
Self: 'small;
fn shorten_lifetime<'small, 'large: 'small>(self) -> Self::Shortened<'small>
where
Self: 'large,
{
self.into_iter()
.map(|(k, v)| (k, v.shorten_lifetime()))
.collect::<HashMap<_, _>>()
}
}
}
pub use helper_utilities::ShortenLifetime;
#[cfg(feature = "derive")]
pub use rust_rel8_derive::TableStruct;
use self::is_nullable::IsNullable;