use std::{
alloc::{alloc, dealloc, handle_alloc_error, Layout},
ptr::drop_in_place,
};
use deadpool_postgres::{Client as DpClient, Transaction as DpTransaction};
use crate::{fetch_client, Error};
use super::{Executable, Query, ToQuery};
struct PinnedClient(pub *mut DpClient);
impl PinnedClient {
unsafe fn from_client(client: DpClient) -> PinnedClient {
let layout = Layout::new::<DpClient>();
let pointer = alloc(layout) as *mut DpClient;
if pointer.is_null() {
handle_alloc_error(layout);
}
pointer.write(client);
PinnedClient(pointer)
}
}
impl Drop for PinnedClient {
fn drop(&mut self) {
unsafe {
drop_in_place(self.0);
dealloc(self.0 as *mut u8, Layout::new::<DpClient>());
}
}
}
pub struct Transaction<'a> {
transaction: DpTransaction<'a>,
_client: PinnedClient,
}
impl<'a> Transaction<'a> {
async fn from_client<'this>(client: DpClient) -> Result<Transaction<'a>, Error> {
let client = unsafe { PinnedClient::from_client(client) };
let transaction = unsafe {
&mut *client.0
}
.transaction()
.await?;
Ok(Transaction {
_client: client,
transaction,
})
}
pub async fn begin() -> Result<Transaction<'a>, Error> {
let client = fetch_client().await?;
Transaction::from_client(client).await
}
pub async fn rollback(self) -> Result<(), Error> {
self.transaction.rollback().await.map_err(Error::from)
}
pub async fn commit(self) -> Result<(), Error> {
self.transaction.commit().await.map_err(Error::from)
}
pub async fn execute<'b, Q, T>(&self, mut query: Q) -> Result<T, Error>
where
Q: ToQuery<'b, T>,
Query<'b, T>: Executable<Output = T>,
{
let query = query.to_query();
query.exec_with(&self.transaction).await
}
}