use std::marker::PhantomData;
use simple_pg_client as postgres;
pub mod protocol;
pub use simple_pg_client::NoTls;
pub use simple_pg_client::{
binary_copy, types,
types::{FromSqlOwned, ToSql},
Error, ErrorKind, Row,
};
pub use simple_pg_macros::{append_sql, sql, test};
pub use simple_pg_pool::PoolConn;
pub use simple_pg_pool::{Config, ManagerConfig, Pool, PoolConfig, RecyclingMethod, Timeouts};
pub mod bindings;
pub type RawConn = simple_pg_client::Client;
pub use simple_pg_client::IsolationLevel;
pub use simple_pg_pool::Conn;
pub use simple_pg_pool::PoolError;
pub use simple_pg_pool::Transaction;
pub use simple_pg_pool::TransactionConfig;
static TEST_RUNTIME: std::sync::OnceLock<tokio::runtime::Runtime> = std::sync::OnceLock::new();
#[doc(hidden)]
pub fn block_on_test_runtime(
test_future: std::pin::Pin<&mut dyn std::future::Future<Output = ()>>,
) {
let rt = TEST_RUNTIME.get_or_init(|| {
tokio::runtime::Builder::new_current_thread()
.enable_all()
.build()
.expect("Failed building the Runtime")
});
rt.block_on(test_future);
}
#[macro_export]
macro_rules! fieldset {
(#[lazy] $pub:vis struct $struct: ident {$first: ident $(,$ident: ident)* $(,)?}) => {
#[repr(C)]
$pub struct $struct {
$pub $first: usize,
$($pub $ident: usize),*
}
#[allow(unused)]
impl $struct {
pub const FIELDS: &'static [&'static str] = &[
stringify!($first),
$(stringify!($ident)),*
];
fn arrays(&mut self) -> (&mut [usize; Self::FIELDS.len()], &[&'static str; Self::FIELDS.len()]) {
unsafe {
(
&mut *(self as *mut $struct as *mut [usize; Self::FIELDS.len()]),
&*(Self::FIELDS.as_ptr() as *const [&'static str; Self::FIELDS.len()])
)
}
}
pub fn lazy_init(&mut self, fields: &[$crate::types::Field]) -> Result<(), $crate::Error> {
if self.$first != usize::MAX {
return Ok(());
}
let (values, names) = self.arrays();
for (i, field) in fields.iter().enumerate() {
for (vi, name) in names.iter().enumerate() {
if *name == field.name() {
values[vi] = i;
break;
}
}
}
let (values, names) = self.arrays();
for (i, value) in values.iter().enumerate() {
if *value == usize::MAX {
return Err($crate::Error::custom(format!("missing field: {}", names[i]).into()))
}
}
return Ok(());
}
}
impl Default for $struct {
fn default() -> Self {
Self {
$first: usize::MAX,
$($ident: usize::MAX),*
}
}
}
};
($pub:vis struct $struct: ident {$first: ident $(,$ident: ident)* $(,)?}) => {
#[repr(C)]
$pub struct $struct {
$pub $first: usize,
$($pub $ident: usize),*
}
};
}
#[derive(Debug)]
pub struct Sql<'a> {
pub raw: &'a str,
pub bindings: &'a [&'a (dyn ToSql + Sync)],
}
impl<'a> Sql<'a> {
pub fn query_string(&self) -> &'a str {
self.raw
}
}
#[derive(Clone, Copy)]
pub enum SqlFragment<'a> {
Raw(&'a str),
Ident(&'a str),
Bindings(&'a [&'a (dyn ToSql + Sync)]),
Binding(&'a (dyn ToSql + Sync)),
Fn(&'a (dyn SqlSnippet<'a> + Sync)),
Lucky(&'a Sql<'a>),
}
pub use SqlFragment::Raw;
impl<'a> SqlSnippet<'a> for SqlFragment<'a> {
fn render_sql(&self, raw: &mut SqlBuilder<'a>) {
raw.push(self)
}
fn borrow_into_sql_fragment(&self) -> Self {
*self
}
}
impl<'a, T1: SqlSnippet<'a>, T2: SqlSnippet<'a>> SqlSnippet<'a> for (T1, T2) {
fn render_sql(&self, raw: &mut SqlBuilder<'a>) {
self.0.render_sql(raw);
self.1.render_sql(raw);
}
}
impl<'a, T1: SqlSnippet<'a>, T2: SqlSnippet<'a>, T3: SqlSnippet<'a>> SqlSnippet<'a>
for (T1, T2, T3)
{
fn render_sql(&self, raw: &mut SqlBuilder<'a>) {
self.0.render_sql(raw);
self.1.render_sql(raw);
self.2.render_sql(raw);
}
}
pub trait SqlSnippet<'a> {
fn render_sql(&self, raw: &mut SqlBuilder<'a>);
fn borrow_into_sql_fragment(&'a self) -> SqlFragment<'a>
where
Self: Sized + Sync,
{
SqlFragment::Fn(self)
}
}
impl<'a, F: Fn(&mut SqlBuilder<'a>)> SqlSnippet<'a> for F {
fn render_sql(&self, raw: &mut SqlBuilder<'a>) {
(self)(raw);
}
}
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 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::Ident(raw) => {
self.raw.push_str(" ");
self.raw.push_str(raw);
self.raw.push_str(" ");
}
SqlFragment::Bindings(binds) => {
self.bindings.extend_from_slice(binds);
}
SqlFragment::Lucky(s) => {
if s.bindings.len() > 0 {
todo!("Not implemented")
}
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 fn add(&mut self, frag: Sql<'a>) {
self.raw.push_str(frag.raw);
self.bindings.extend_from_slice(frag.bindings);
}
}
pub struct GeneratedSql<'a> {
pub raw: String,
pub bindings: Vec<&'a (dyn ToSql + Sync)>,
}
mod impl_pool_client;
impl<'a> GeneratedSql<'a> {
pub fn query_string(&self) -> &str {
&self.raw
}
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,
}
}
}
pub struct JoinSql<'a, T> {
list: &'a [T],
sep: Option<SqlFragment<'a>>,
}
impl<'a, T> JoinSql<'a, T> {
pub fn new(list: &'a [T]) -> Self {
JoinSql { list, sep: None }
}
pub fn with_seperator(self, sep: SqlFragment<'a>) -> Self {
JoinSql {
list: self.list,
sep: Some(sep),
}
}
}
impl<'a, T> SqlSnippet<'a> for JoinSql<'a, T>
where
&'a T: SqlSnippet<'a>,
{
fn render_sql(&self, builder: &mut SqlBuilder<'a>) {
let mut parts = self.list.iter();
if let Some(part) = parts.next() {
part.render_sql(builder);
}
for part in parts {
if let Some(sep) = &self.sep {
builder.push(sep);
}
part.render_sql(builder);
}
}
}