Skip to content

Commit

Permalink
Implement dynamic binding for various query impls
Browse files Browse the repository at this point in the history
  • Loading branch information
mara-schulke committed Nov 4, 2023
1 parent 133bab3 commit d17ab2d
Show file tree
Hide file tree
Showing 4 changed files with 91 additions and 50 deletions.
38 changes: 33 additions & 5 deletions atmosphere-core/src/bind.rs
Original file line number Diff line number Diff line change
@@ -1,18 +1,46 @@
use crate::{Column, Result, Table};
use sqlx::query::QueryAs;
use sqlx::Encode;
use sqlx::Type;
use sqlx::{database::HasArguments, Database};

type Query<'q, DB> = sqlx::query::Query<'q, DB, <DB as HasArguments<'q>>::Arguments>;

pub trait Bindable<'q, DB>
where
DB: Database + for<'a> HasArguments<'a>,
{
fn dyn_bind<T: 'q + Send + Encode<'q, DB> + Type<DB>>(self, value: T) -> Self;
}

impl<'q, DB> Bindable<'q, DB> for Query<'q, DB>
where
DB: Database + for<'a> HasArguments<'a>,
{
fn dyn_bind<T: 'q + Send + Encode<'q, DB> + Type<DB>>(self, value: T) -> Self {
self.bind(value)
}
}

impl<'q, E, DB> Bindable<'q, DB> for QueryAs<'q, DB, E, <DB as HasArguments<'q>>::Arguments>
where
DB: Database + for<'a> HasArguments<'a>,
{
fn dyn_bind<T: 'q + Send + Encode<'q, DB> + Type<DB>>(self, value: T) -> Self {
self.bind(value)
}
}

/// Bind columns to SQL Queries
pub trait Bind<DB: Database>: Table
where
DB: Database,
{
/// Bind a single column to the query
fn bind<'q>(&'q self, c: &'q Column<Self>, query: Query<'q, DB>) -> Result<Query<'q, DB>>;
fn bind<'q, Q: Bindable<'q, DB>>(&'q self, c: &'q Column<Self>, query: Q) -> Result<Q>;

/// Bind a all columns to the query
fn bind_all<'q>(&'q self, mut query: Query<'q, DB>) -> Result<Query<'q, DB>> {
fn bind_all<'q, Q: Bindable<'q, DB>>(&'q self, mut query: Q) -> Result<Q> {
query = self.bind_primary_key(query)?;
query = self.bind_foreign_keys(query)?;
query = self.bind_data(query)?;
Expand All @@ -21,12 +49,12 @@ where
}

/// Bind the primary key column to the query
fn bind_primary_key<'q>(&'q self, query: Query<'q, DB>) -> Result<Query<'q, DB>> {
fn bind_primary_key<'q, Q: Bindable<'q, DB>>(&'q self, query: Q) -> Result<Q> {
self.bind(&Self::PRIMARY_KEY, query)
}

/// Bind the foreign keys columns to the query
fn bind_foreign_keys<'q>(&'q self, mut query: Query<'q, DB>) -> Result<Query<'q, DB>> {
fn bind_foreign_keys<'q, Q: Bindable<'q, DB>>(&'q self, mut query: Q) -> Result<Q> {
for ref fk in Self::FOREIGN_KEYS {
query = self.bind(fk, query)?;
}
Expand All @@ -35,7 +63,7 @@ where
}

/// Bind the data columns to the query
fn bind_data<'q>(&'q self, mut query: Query<'q, DB>) -> Result<Query<'q, DB>> {
fn bind_data<'q, Q: Bindable<'q, DB>>(&'q self, mut query: Q) -> Result<Q> {
for ref data in Self::DATA {
query = self.bind(data, query)?;
}
Expand Down
2 changes: 1 addition & 1 deletion atmosphere-core/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,5 +7,5 @@ pub mod schema;
/// Automated testing of SQL interactions
pub mod testing;

pub use bind::Bind;
pub use bind::*;
pub use schema::*;
84 changes: 45 additions & 39 deletions atmosphere-core/src/schema.rs
Original file line number Diff line number Diff line change
Expand Up @@ -62,11 +62,19 @@ where
}

#[async_trait]
pub trait Read: Table {
pub trait Read: Table + Send + Sync + Unpin + 'static {
/// Find a row by its primary key
async fn find<'e, E>(pk: &Self::PrimaryKey, executor: E) -> sqlx::Result<Self>
where
Self: Bind<sqlx::Postgres> + Sync + 'static + Unpin,
Self: Bind<sqlx::Postgres>,
E: sqlx::Executor<'e, Database = sqlx::Postgres>,
for<'q> <sqlx::Postgres as sqlx::database::HasArguments<'q>>::Arguments:
Send + sqlx::IntoArguments<'q, sqlx::Postgres>;

/// Reload from database
async fn reload<'e, E>(&mut self, executor: E) -> sqlx::Result<()>
where
Self: Bind<sqlx::Postgres>,
E: sqlx::Executor<'e, Database = sqlx::Postgres>,
for<'q> <sqlx::Postgres as sqlx::database::HasArguments<'q>>::Arguments:
Send + sqlx::IntoArguments<'q, sqlx::Postgres>;
Expand All @@ -87,35 +95,48 @@ pub trait Read: Table {
#[async_trait]
impl<T> Read for T
where
T: Table,
T: Table + Send + Sync + Unpin + 'static,
{
async fn find<'e, E>(pk: &Self::PrimaryKey, executor: E) -> sqlx::Result<Self>
where
Self: Bind<sqlx::Postgres> + Sync + 'static + Unpin,
Self: Bind<sqlx::Postgres>,
E: sqlx::Executor<'e, Database = sqlx::Postgres>,
for<'q> <sqlx::Postgres as sqlx::database::HasArguments<'q>>::Arguments:
Send + sqlx::IntoArguments<'q, sqlx::Postgres>,
{
let query = crate::runtime::sql::SQL::<T, sqlx::Postgres>::select();

Ok(sqlx::query_as::<sqlx::Postgres, Self>(&query.into_sql())
sqlx::query_as::<sqlx::Postgres, Self>(&query.into_sql())
.bind(pk)
.fetch_one(executor)
.await
.unwrap())
}

async fn reload<'e, E>(&mut self, executor: E) -> sqlx::Result<()>
where
Self: Bind<sqlx::Postgres> + Sync + Unpin + 'static,
E: sqlx::Executor<'e, Database = sqlx::Postgres>,
for<'q> <sqlx::Postgres as sqlx::database::HasArguments<'q>>::Arguments:
Send + sqlx::IntoArguments<'q, sqlx::Postgres>,
{
let sql = crate::runtime::sql::SQL::<T, sqlx::Postgres>::select().into_sql();

let query = sqlx::query_as::<sqlx::Postgres, Self>(&sql);

let new = self
.bind_primary_key(query)
.unwrap()
.fetch_one(executor)
.await?;

*self = new;

Ok(())
}
}

#[async_trait]
pub trait Update: Table {
// Reload from database
//async fn reload<'e, E>(&mut self, executor: E) -> sqlx::Result<()>
//where
//Self: Bind<sqlx::Postgres> + Sync + 'static,
//E: sqlx::Executor<'e, Database = sqlx::Postgres>,
//for<'q> <sqlx::Postgres as sqlx::database::HasArguments<'q>>::Arguments:
//Send + sqlx::IntoArguments<'q, sqlx::Postgres>;

pub trait Update: Table + Send + Sync + Unpin + 'static {
/// Update the row in the database
async fn update<'e, E>(&mut self, executor: E) -> sqlx::Result<PgQueryResult>
where
Expand All @@ -136,26 +157,11 @@ pub trait Update: Table {
#[async_trait]
impl<T> Update for T
where
T: Table,
T: Table + Send + Sync + Unpin + 'static,
{
//async fn reload<'e, E>(&self, executor: E) -> Result<PgQueryResult>
//where
//Self: Bind<sqlx::Postgres> + Sync + 'static,
//E: sqlx::Executor<'e, Database = sqlx::Postgres>,
//for<'q> <sqlx::Postgres as sqlx::database::HasArguments<'q>>::Arguments:
//Send + sqlx::IntoArguments<'q, sqlx::Postgres>,
//{
//let query = crate::runtime::sql::SQL::<T, sqlx::Postgres>::delete();

//self.bind_all(sqlx::query::<sqlx::Postgres>(&query.into_sql()))?
//.execute(executor)
//.await
//.map_err(|_| ())
//}

async fn update<'e, E>(&mut self, executor: E) -> sqlx::Result<PgQueryResult>
where
Self: Bind<sqlx::Postgres> + Sync + 'static,
Self: Bind<sqlx::Postgres>,
E: sqlx::Executor<'e, Database = sqlx::Postgres>,
for<'q> <sqlx::Postgres as sqlx::database::HasArguments<'q>>::Arguments:
Send + sqlx::IntoArguments<'q, sqlx::Postgres>,
Expand All @@ -168,7 +174,7 @@ where

async fn save<'e, E>(&mut self, executor: E) -> sqlx::Result<PgQueryResult>
where
Self: Bind<sqlx::Postgres> + Sync + 'static,
Self: Bind<sqlx::Postgres>,
E: sqlx::Executor<'e, Database = sqlx::Postgres>,
for<'q> <sqlx::Postgres as sqlx::database::HasArguments<'q>>::Arguments:
Send + sqlx::IntoArguments<'q, sqlx::Postgres>,
Expand All @@ -181,19 +187,19 @@ where
}

#[async_trait]
pub trait Delete: Table {
pub trait Delete: Table + Send + Sync + Unpin + 'static {
/// Delete row in database
async fn delete<'e, E>(&self, executor: E) -> sqlx::Result<PgQueryResult>
where
Self: Bind<sqlx::Postgres> + Sync + 'static,
Self: Bind<sqlx::Postgres>,
E: sqlx::Executor<'e, Database = sqlx::Postgres>,
for<'q> <sqlx::Postgres as sqlx::database::HasArguments<'q>>::Arguments:
Send + sqlx::IntoArguments<'q, sqlx::Postgres>;

/// Delete row in database by primary key
async fn delete_by<'e, E>(pk: &Self::PrimaryKey, executor: E) -> sqlx::Result<PgQueryResult>
where
Self: Bind<sqlx::Postgres> + Sync + 'static,
Self: Bind<sqlx::Postgres>,
E: sqlx::Executor<'e, Database = sqlx::Postgres>,
for<'q> <sqlx::Postgres as sqlx::database::HasArguments<'q>>::Arguments:
Send + sqlx::IntoArguments<'q, sqlx::Postgres>;
Expand All @@ -210,11 +216,11 @@ pub trait Delete: Table {
#[async_trait]
impl<T> Delete for T
where
T: Table,
T: Table + Send + Sync + Unpin + 'static,
{
async fn delete<'e, E>(&self, executor: E) -> sqlx::Result<PgQueryResult>
where
Self: Bind<sqlx::Postgres> + Sync + 'static,
Self: Bind<sqlx::Postgres>,
E: sqlx::Executor<'e, Database = sqlx::Postgres>,
for<'q> <sqlx::Postgres as sqlx::database::HasArguments<'q>>::Arguments:
Send + sqlx::IntoArguments<'q, sqlx::Postgres>,
Expand All @@ -229,7 +235,7 @@ where

async fn delete_by<'e, E>(pk: &Self::PrimaryKey, executor: E) -> sqlx::Result<PgQueryResult>
where
Self: Bind<sqlx::Postgres> + Sync + 'static,
Self: Bind<sqlx::Postgres>,
E: sqlx::Executor<'e, Database = sqlx::Postgres>,
for<'q> <sqlx::Postgres as sqlx::database::HasArguments<'q>>::Arguments:
Send + sqlx::IntoArguments<'q, sqlx::Postgres>,
Expand Down
17 changes: 12 additions & 5 deletions atmosphere-macros/src/schema/table.rs
Original file line number Diff line number Diff line change
Expand Up @@ -123,7 +123,9 @@ impl Table {

quote!(
if #col.name == Self::PRIMARY_KEY.name {
return Ok(#query.bind(&self.#name));
use ::atmosphere_core::Bindable;

return Ok(#query.dyn_bind(&self.#name));
}
)
};
Expand All @@ -137,7 +139,9 @@ impl Table {

stream.extend(quote!(
if #col.name == #name {
return Ok(#query.bind(&self.#ident));
use ::atmosphere_core::Bindable;

return Ok(#query.dyn_bind(&self.#ident));
}
));
}
Expand All @@ -154,7 +158,9 @@ impl Table {

stream.extend(quote!(
if #col.name == #name {
return Ok(#query.bind(&self.#ident));
use ::atmosphere_core::Bindable;

return Ok(#query.dyn_bind(&self.#ident));
}
));
}
Expand All @@ -168,11 +174,12 @@ impl Table {
impl ::atmosphere::Bind<#databases> for #ident {
fn bind<
'q,
Q: ::atmosphere::Bindable<'q, #databases>
>(
&'q self,
#col: &'q ::atmosphere::Column<Self>,
#query: ::sqlx::query::Query<'q, #databases, <#databases as ::sqlx::database::HasArguments<'q>>::Arguments>,
) -> ::atmosphere::Result<::sqlx::query::Query<'q, #databases, <#databases as ::sqlx::database::HasArguments<'q>>::Arguments>> {
#query: Q
) -> ::atmosphere::Result<Q> {
#primary_key_bind
#foreign_key_binds
#data_binds
Expand Down

0 comments on commit d17ab2d

Please sign in to comment.