use std::marker::PhantomData;
use super::{Select, SelectBody, Selection};
use crate::dialect::Dialect;
use crate::expr::Value;
use crate::render::{QuerySink, SelectItem};
pub struct DynSelect<D, Output> {
body: SelectBody,
selection: Vec<SelectItem>,
_marker: PhantomData<fn() -> (D, Output)>,
}
impl<D, Scope, Sel> Select<D, Scope, Sel> {
pub fn erase<Idx>(self) -> DynSelect<D, Sel::Output>
where
Sel: Selection<Scope, Idx>,
{
DynSelect {
body: self.body,
selection: self.selection.items(),
_marker: PhantomData,
}
}
}
#[diagnostic::on_unimplemented(
message = "an erased query can't be filtered",
label = "add `.filter(..)` before `.erase()` — erasure gives up the scope a condition is checked against",
note = "`.erase()` is for unifying two fully-built branches with different joins; compose the query first"
)]
pub trait CannotFilterAfterErase {}
impl<D, Output> DynSelect<D, Output> {
#[doc(hidden)]
pub fn filter<T: CannotFilterAfterErase>(self, _cond: T) -> Self {
self
}
pub fn limit(mut self, n: impl super::IntoRowCount) -> Self {
self.body.limit = Some(n.into_row_count());
self
}
pub fn offset(mut self, n: impl super::IntoRowCount) -> Self {
self.body.offset = Some(n.into_row_count());
self
}
}
impl<D: Dialect, Output> DynSelect<D, Output> {
pub fn count_sql(&self, _dialect: D) -> (String, Vec<Value>) {
self.body.count_sql::<D>(&self.selection)
}
pub fn to_sql(&self, _dialect: D) -> (String, Vec<Value>) {
let mut sink = QuerySink::<D>::new();
self.body.render_into::<D>(&self.selection, &mut sink);
sink.finish()
}
}