use std::{iter::Map, marker::PhantomData};
use postgres::{types::{ToSql, FromSqlOwned}, Row, CopyInWriter};
pub use postgres_extras_macros::sql;
#[derive(Debug)]
pub struct Sql<'a> {
pub raw: &'a str,
pub bindings: &'a [&'a(dyn ToSql + Sync) ]
}
impl<'a> Sql<'a> {
pub fn fragment(&self) -> SqlFragment<'_> {
SqlFragment::Lucky(self)
}
pub fn execute(self, tx: &mut impl postgres::GenericClient) -> Result<u64, postgres::error::Error> {
tx.execute(self.raw, self.bindings)
}
pub fn batch_execute(self, tx: &mut impl postgres::GenericClient) -> Result<(), postgres::error::Error> {
tx.batch_execute(self.raw)
}
pub fn query(self, tx: &mut impl postgres::GenericClient)-> Result<Vec<Row>, postgres::error::Error> {
tx.query(self.raw, self.bindings)
}
pub fn query_one(self, tx: &mut impl postgres::GenericClient)-> Result<Row, postgres::error::Error> {
tx.query_one(self.raw, self.bindings)
}
pub fn query_into<T: FromSqlOwned>(self, tx: &mut impl postgres::GenericClient)-> Result<T, postgres::error::Error> {
Ok(tx.query_one(self.raw, self.bindings)?.get(0))
}
pub fn query_opt(self, tx: &mut impl postgres::GenericClient)-> Result<Option<Row>, postgres::error::Error> {
tx.query_opt(self.raw, self.bindings)
}
pub fn copy_in(self, tx: &mut impl postgres::GenericClient) -> Result<CopyInWriter<'_>, postgres::error::Error> {
tx.copy_in(self.raw)
}
}
#[derive(Clone, Copy)]
pub enum SqlFragment<'a> {
Raw(&'a str),
Bindings(&'a [ &'a (dyn ToSql + Sync) ]),
Binding(&'a (dyn ToSql + Sync)),
Fn(&'a dyn SqlSnippet<'a>),
Lucky(&'a Sql<'a>),
}
impl<'a> SqlFragment<'a> {
pub fn borrow_into_sql_fragment(self) -> Self {
self
}
}
pub trait SqlSnippet<'a> {
fn render_sql(&self, raw: &mut SqlBuilder<'a>);
}
pub type Binding<'a> = &'a (dyn ToSql+Sync);
#[derive(Clone)]
pub struct MapIntoRow<'a, const N: usize, T>{
iter: T,
s: PhantomData<&'a ()>
}
impl<'a, const N: usize, T> Iterator for MapIntoRow<'a, N, T>
where T: Iterator,
<T as Iterator>::Item: IntoTupleSlice<'a, N>
{
type Item = [Binding<'a>; N];
fn next(&mut self) -> Option<Self::Item> {
Some(self.iter.next()?.sql_tuple())
}
}
pub trait SqlIterExt: Iterator + Sized {
fn map_into_row<'a, const N: usize, F: FnMut(Self::Item) -> [Binding<'a>; N]>(self, fnx: F) -> Map<Self, F> {
self.map(fnx)
}
fn into_sql_tuples<'a, const N: usize>(self) -> MapIntoRow<'a, N, Self>
where Self::Item: IntoTupleSlice<'a, N>
{
MapIntoRow{
iter: self,
s: PhantomData
}
}
fn borrow_into_sql_fragment<'a>(&'a self) -> SqlFragment<'a>
where Self: IntoIterator + Clone, <Self as IntoIterator>::Item : SqlSnippet<'a>
{
SqlFragment::Fn(unsafe {std::mem::transmute::<&'a Self, &'a IterValues<Self>>(self)})
}
}
impl<T: Iterator + Sized> SqlIterExt for T{}
pub trait IntoTupleSlice<'a, const N: usize> {
fn sql_tuple(&self) -> [Binding<'a>; N];
}
macro_rules! impl_into_tuple_slice {
($count: literal; $($T:ident : $N: tt),*) => {
impl<'a, $($T: ToSql + Sync,)*> IntoTupleSlice<'a, $count> for ($(&'a $T,)*) {
fn sql_tuple(&self) -> [Binding<'a>; $count] {
[$(self.$N),*]
}
}
impl<'a, $($T: ToSql + Sync,)*> IntoTupleSlice<'a, $count> for &'a ($($T,)*) {
fn sql_tuple(&self) -> [Binding<'a>; $count] {
[$(&self.$N),*]
}
}
};
}
impl_into_tuple_slice!(1; T0: 0);
impl_into_tuple_slice!(2; T0: 0, T1: 1);
impl_into_tuple_slice!(3; T0: 0, T1: 1, T2: 2);
impl_into_tuple_slice!(4; T0: 0, T1: 1, T2: 2, T3: 3);
impl_into_tuple_slice!(5; T0: 0, T1: 1, T2: 2, T3: 3, T4: 4);
impl_into_tuple_slice!(6; T0: 0, T1: 1, T2: 2, T3: 3, T4: 4, T5: 5);
impl_into_tuple_slice!(7; T0: 0, T1: 1, T2: 2, T3: 3, T4: 4, T5: 5, T6: 6);
impl_into_tuple_slice!(8; T0: 0, T1: 1, T2: 2, T3: 3, T4: 4, T5: 5, T6: 6, T7: 7);
#[repr(transparent)]
pub struct IterValues<T> { pub iter: T}
impl <'a ,T: IntoIterator + Clone> SqlSnippet<'a> for IterValues<T>
where <T as IntoIterator>::Item : SqlSnippet<'a>
{
fn render_sql(&self, sql: &mut SqlBuilder<'a>) {
for (x,i) in self.iter.clone().into_iter().enumerate() {
if x > 0 {
sql.raw.push(',');
}
i.render_sql(sql)
}
}
}
impl <'a, const N: usize,> SqlSnippet<'a> for [ Binding<'a>;N ] {
fn render_sql(&self, sql: &mut SqlBuilder<'a>) {
sql.raw.push('(');
for (x, i)in self.iter().enumerate() {
if x > 0 {
sql.raw.push(',');
}
sql.push_binding(*i);
}
sql.raw.push(')');
}
}
#[derive(Default)]
pub struct SqlBuilder<'a> {
pub raw: String,
pub bindings: Vec<&'a(dyn ToSql + Sync)>
}
impl<'a> SqlBuilder<'a> {
pub fn push_binding(&mut self, bindings: &'a (dyn ToSql + Sync)) {
use std::fmt::Write;
self.bindings.push(bindings);
write!(self.raw, "${} ", self.bindings.len()).ok();
}
pub fn push(&mut self, frag: &SqlFragment<'a>) {
match frag {
SqlFragment::Raw(raw) => {
self.raw.push_str(raw);
},
SqlFragment::Bindings(binds) => {
self.bindings.extend_from_slice(binds);
},
SqlFragment::Lucky(s) => {
self.raw.push_str(s.raw);
self.bindings.extend_from_slice(s.bindings);
},
SqlFragment::Fn(fnx) => {
fnx.render_sql(self);
},
SqlFragment::Binding(binding) => {
self.push_binding(*binding)
},
}
}
}
pub struct GeneratedSql<'a> {
pub raw: String,
pub bindings: Vec<&'a(dyn ToSql + Sync)>
}
impl<'a> GeneratedSql<'a> {
pub fn execute(self, tx: &mut impl postgres::GenericClient) -> Result<u64, postgres::error::Error> {
tx.execute(&self.raw, &self.bindings)
}
pub fn batch_execute(self, tx: &mut impl postgres::GenericClient) -> Result<(), postgres::error::Error> {
tx.batch_execute(&self.raw)
}
pub fn query(self, tx: &mut impl postgres::GenericClient)-> Result<Vec<Row>, postgres::error::Error> {
tx.query(&self.raw, &self.bindings)
}
pub fn query_one(self, tx: &mut impl postgres::GenericClient)-> Result<Row, postgres::error::Error> {
tx.query_one(&self.raw, &self.bindings)
}
pub fn query_into<T: FromSqlOwned>(self, tx: &mut impl postgres::GenericClient)-> Result<T, postgres::error::Error> {
Ok(tx.query_one(&self.raw, &self.bindings)?.get(0))
}
pub fn query_opt(self, tx: &mut impl postgres::GenericClient)-> Result<Option<Row>, postgres::error::Error> {
tx.query_opt(&self.raw, &self.bindings)
}
pub fn copy_in(self, tx: &mut impl postgres::GenericClient) -> Result<CopyInWriter<'_>, postgres::error::Error> {
tx.copy_in(&self.raw)
}
}
impl<'a> GeneratedSql<'a> {
pub fn generated(fragments: &[ SqlFragment<'a>]) -> GeneratedSql<'a> {
let mut builder = SqlBuilder::default();
for frag in fragments {
builder.push(frag)
}
GeneratedSql { raw: builder.raw, bindings: builder.bindings }
}
}