1use std::{iter::Map, marker::PhantomData};
2
3use postgres::{types::{ToSql, FromSqlOwned}, Row, CopyInWriter};
4pub use postgres_extras_macros::sql;
5
6#[derive(Debug)]
7pub struct Sql<'a> {
8 pub raw: &'a str,
9 pub bindings: &'a [&'a(dyn ToSql + Sync) ]
10}
11
12impl<'a> Sql<'a> {
13 pub fn fragment(&self) -> SqlFragment<'_> {
14 SqlFragment::Lucky(self)
15 }
16 pub fn execute(self, tx: &mut impl postgres::GenericClient) -> Result<u64, postgres::error::Error> {
17 tx.execute(self.raw, self.bindings)
18 }
19 pub fn batch_execute(self, tx: &mut impl postgres::GenericClient) -> Result<(), postgres::error::Error> {
20 tx.batch_execute(self.raw)
21 }
22 pub fn query(self, tx: &mut impl postgres::GenericClient)-> Result<Vec<Row>, postgres::error::Error> {
23 tx.query(self.raw, self.bindings)
24 }
25 pub fn query_one(self, tx: &mut impl postgres::GenericClient)-> Result<Row, postgres::error::Error> {
26 tx.query_one(self.raw, self.bindings)
27 }
28 pub fn query_into<T: FromSqlOwned>(self, tx: &mut impl postgres::GenericClient)-> Result<T, postgres::error::Error> {
29 Ok(tx.query_one(self.raw, self.bindings)?.get(0))
30 }
31 pub fn query_opt(self, tx: &mut impl postgres::GenericClient)-> Result<Option<Row>, postgres::error::Error> {
32 tx.query_opt(self.raw, self.bindings)
33 }
34 pub fn copy_in(self, tx: &mut impl postgres::GenericClient) -> Result<CopyInWriter<'_>, postgres::error::Error> {
35 tx.copy_in(self.raw)
36 }
37}
38#[derive(Clone, Copy)]
39pub enum SqlFragment<'a> {
40 Raw(&'a str),
41 Bindings(&'a [ &'a (dyn ToSql + Sync) ]),
42 Binding(&'a (dyn ToSql + Sync)),
43 Fn(&'a dyn SqlSnippet<'a>),
44 Lucky(&'a Sql<'a>),
45}
46
47impl<'a> SqlFragment<'a> {
48 pub fn borrow_into_sql_fragment(self) -> Self {
49 self
50 }
51}
52pub trait SqlSnippet<'a> {
53 fn render_sql(&self, raw: &mut SqlBuilder<'a>);
54}
55pub type Binding<'a> = &'a (dyn ToSql+Sync);
56#[derive(Clone)]
57pub struct MapIntoRow<'a, const N: usize, T>{
58 iter: T,
59 s: PhantomData<&'a ()>
60}
61impl<'a, const N: usize, T> Iterator for MapIntoRow<'a, N, T>
62where T: Iterator,
63 <T as Iterator>::Item: IntoTupleSlice<'a, N>
64{
65 type Item = [Binding<'a>; N];
66
67 fn next(&mut self) -> Option<Self::Item> {
68 Some(self.iter.next()?.sql_tuple())
69 }
70
71}
72
73pub trait SqlIterExt: Iterator + Sized {
74 fn map_into_row<'a, const N: usize, F: FnMut(Self::Item) -> [Binding<'a>; N]>(self, fnx: F) -> Map<Self, F> {
75 self.map(fnx)
76 }
77 fn into_sql_tuples<'a, const N: usize>(self) -> MapIntoRow<'a, N, Self>
78 where Self::Item: IntoTupleSlice<'a, N>
79 {
80 MapIntoRow{
81 iter: self,
82 s: PhantomData
83 }
84 }
85
86 fn borrow_into_sql_fragment<'a>(&'a self) -> SqlFragment<'a>
87 where Self: IntoIterator + Clone, <Self as IntoIterator>::Item : SqlSnippet<'a>
88 {
89 SqlFragment::Fn(unsafe {std::mem::transmute::<&'a Self, &'a IterValues<Self>>(self)})
90 }
91}
92
93impl<T: Iterator + Sized> SqlIterExt for T{}
94
95pub trait IntoTupleSlice<'a, const N: usize> {
96 fn sql_tuple(&self) -> [Binding<'a>; N];
97}
98
99macro_rules! impl_into_tuple_slice {
100 ($count: literal; $($T:ident : $N: tt),*) => {
101 impl<'a, $($T: ToSql + Sync,)*> IntoTupleSlice<'a, $count> for ($(&'a $T,)*) {
102 fn sql_tuple(&self) -> [Binding<'a>; $count] {
103 [$(self.$N),*]
104 }
105 }
106 impl<'a, $($T: ToSql + Sync,)*> IntoTupleSlice<'a, $count> for &'a ($($T,)*) {
107 fn sql_tuple(&self) -> [Binding<'a>; $count] {
108 [$(&self.$N),*]
109 }
110 }
111 };
112}
113
114impl_into_tuple_slice!(1; T0: 0);
115impl_into_tuple_slice!(2; T0: 0, T1: 1);
116impl_into_tuple_slice!(3; T0: 0, T1: 1, T2: 2);
117impl_into_tuple_slice!(4; T0: 0, T1: 1, T2: 2, T3: 3);
118impl_into_tuple_slice!(5; T0: 0, T1: 1, T2: 2, T3: 3, T4: 4);
119impl_into_tuple_slice!(6; T0: 0, T1: 1, T2: 2, T3: 3, T4: 4, T5: 5);
120impl_into_tuple_slice!(7; T0: 0, T1: 1, T2: 2, T3: 3, T4: 4, T5: 5, T6: 6);
121impl_into_tuple_slice!(8; T0: 0, T1: 1, T2: 2, T3: 3, T4: 4, T5: 5, T6: 6, T7: 7);
122
123
124
125#[repr(transparent)]
126pub struct IterValues<T> { pub iter: T}
127impl <'a ,T: IntoIterator + Clone> SqlSnippet<'a> for IterValues<T>
128 where <T as IntoIterator>::Item : SqlSnippet<'a>
129{
130 fn render_sql(&self, sql: &mut SqlBuilder<'a>) {
131 for (x,i) in self.iter.clone().into_iter().enumerate() {
132 if x > 0 {
133 sql.raw.push(',');
134 }
135 i.render_sql(sql)
136 }
137 }
138}
139impl <'a, const N: usize,> SqlSnippet<'a> for [ Binding<'a>;N ] {
140 fn render_sql(&self, sql: &mut SqlBuilder<'a>) {
141 sql.raw.push('(');
142 for (x, i)in self.iter().enumerate() {
143 if x > 0 {
144 sql.raw.push(',');
145 }
146 sql.push_binding(*i);
147 }
148 sql.raw.push(')');
149 }
150}
151
152#[derive(Default)]
153pub struct SqlBuilder<'a> {
154 pub raw: String,
155 pub bindings: Vec<&'a(dyn ToSql + Sync)>
156}
157
158impl<'a> SqlBuilder<'a> {
159 pub fn push_binding(&mut self, bindings: &'a (dyn ToSql + Sync)) {
161 use std::fmt::Write;
162 self.bindings.push(bindings);
163 write!(self.raw, "${} ", self.bindings.len()).ok();
164 }
165
166 pub fn push(&mut self, frag: &SqlFragment<'a>) {
167 match frag {
168 SqlFragment::Raw(raw) => {
169 self.raw.push_str(raw);
170 },
171 SqlFragment::Bindings(binds) => {
172 self.bindings.extend_from_slice(binds);
173 },
174 SqlFragment::Lucky(s) => {
175 self.raw.push_str(s.raw);
176 self.bindings.extend_from_slice(s.bindings);
177 },
178 SqlFragment::Fn(fnx) => {
179 fnx.render_sql(self);
180 },
181 SqlFragment::Binding(binding) => {
182 self.push_binding(*binding)
183 },
184 }
185 }
186}
187pub struct GeneratedSql<'a> {
188 pub raw: String,
189 pub bindings: Vec<&'a(dyn ToSql + Sync)>
190}
191
192impl<'a> GeneratedSql<'a> {
193 pub fn execute(self, tx: &mut impl postgres::GenericClient) -> Result<u64, postgres::error::Error> {
194 tx.execute(&self.raw, &self.bindings)
195 }
196 pub fn batch_execute(self, tx: &mut impl postgres::GenericClient) -> Result<(), postgres::error::Error> {
197 tx.batch_execute(&self.raw)
198 }
199 pub fn query(self, tx: &mut impl postgres::GenericClient)-> Result<Vec<Row>, postgres::error::Error> {
200 tx.query(&self.raw, &self.bindings)
201 }
202 pub fn query_one(self, tx: &mut impl postgres::GenericClient)-> Result<Row, postgres::error::Error> {
203 tx.query_one(&self.raw, &self.bindings)
204 }
205 pub fn query_into<T: FromSqlOwned>(self, tx: &mut impl postgres::GenericClient)-> Result<T, postgres::error::Error> {
206 Ok(tx.query_one(&self.raw, &self.bindings)?.get(0))
207 }
208 pub fn query_opt(self, tx: &mut impl postgres::GenericClient)-> Result<Option<Row>, postgres::error::Error> {
209 tx.query_opt(&self.raw, &self.bindings)
210 }
211 pub fn copy_in(self, tx: &mut impl postgres::GenericClient) -> Result<CopyInWriter<'_>, postgres::error::Error> {
212 tx.copy_in(&self.raw)
213 }
214}
215impl<'a> GeneratedSql<'a> {
216 pub fn generated(fragments: &[ SqlFragment<'a>]) -> GeneratedSql<'a> {
217 let mut builder = SqlBuilder::default();
218 for frag in fragments {
219 builder.push(frag)
220 }
221 GeneratedSql { raw: builder.raw, bindings: builder.bindings }
222 }
223}