Skip to content

Commit

Permalink
don't double boxing prepare future
Browse files Browse the repository at this point in the history
  • Loading branch information
fakeshadow committed Sep 28, 2024
1 parent 612d2a7 commit a9fd5e8
Show file tree
Hide file tree
Showing 5 changed files with 46 additions and 7 deletions.
1 change: 1 addition & 0 deletions postgres-codegen/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -14,3 +14,4 @@ proc-macro = true
proc-macro2 = "1"
syn = { version = "2", features = ["full"] }
quote = "1.0"
pg_query = { version = "5" }
19 changes: 16 additions & 3 deletions postgres-codegen/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,15 @@
use quote::quote;
use syn::{
parse::{Parse, ParseStream},
spanned::Spanned,
token::Comma,
Expr, ExprReference, Lit, LitStr,
};

#[proc_macro]
pub fn sql(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
let Query { sql, exprs, types } = syn::parse_macro_input!(input as Query);

quote! {
::xitca_postgres::statement::Statement::unnamed(#sql, &[#(#types),*])
.bind_dyn(&[#(#exprs),*])
Expand All @@ -26,19 +28,30 @@ struct Query {

impl Parse for Query {
fn parse(input: ParseStream) -> syn::Result<Self> {
let sql = input.parse()?;
let sql = input.parse::<LitStr>()?;

pg_query::parse(&sql.value()).map_err(|e| syn::Error::new(sql.span(), e.to_string()))?;

let mut exprs = Vec::new();
let mut types = Vec::new();

while input.parse::<Comma>().is_ok() {
let expr = input.parse::<ExprReference>()?;
let Expr::Lit(lit) = expr.expr.as_ref() else {
unimplemented!("example macro can only handle literal references")
return Err(syn::Error::new(
expr.span(),
"example can only handle literal expression",
));
};
let ty = match lit.lit {
Lit::Str(_) => quote! { ::xitca_postgres::types::Type::TEXT },
Lit::Int(_) => quote! { ::xitca_postgres::types::Type::INT4 },
_ => unimplemented!("example macro only supports string and i32 as query parameters"),
ref lit => {
return Err(syn::Error::new(
lit.span(),
"example can only handle i32 and String type as parameter",
))
}
};
types.push(ty);
exprs.push(expr);
Expand Down
3 changes: 3 additions & 0 deletions postgres/examples/macro.rs
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,9 @@ async fn main() -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
// it's also possible to utilize xitca-postgres's Execute traits for more customizable macro usage
let mut stream = sql!("SELECT * FROM foo WHERE id = $1 AND name = $2", &1i32, &"alice").query(&cli)?;

// the macro also have basic function for sql validation check. try uncomment below to see compile error.
// let _ = sql!("SELECT * FRO foo WHERR id = $1 AN name = $2", &1i32, &"alice");

let row = stream.try_next().await?.ok_or("row not found")?;

assert_eq!(row.get::<&str>("name"), "alice");
Expand Down
2 changes: 1 addition & 1 deletion postgres/src/driver/generic.rs
Original file line number Diff line number Diff line change
Expand Up @@ -298,7 +298,7 @@ where
Err(e) => return Err(e),
}
}
_ => unreachable!("try_write must be called when WriteState is waiting"),
_ => unreachable!("try_write must not be called when WriteState is wait or closed"),
}
}

Expand Down
28 changes: 25 additions & 3 deletions postgres/src/execute.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ use super::{
Statement, StatementCreate, StatementCreateBlocking, StatementGuarded, StatementNamed, StatementQuery,
StatementUnnamedBind, StatementUnnamedQuery,
},
BoxedFuture,
};

/// Defining how a query is executed. can be used for customizing encoding, executing and database
Expand Down Expand Up @@ -120,19 +121,40 @@ where
}
}

type IntoGuardedFuture<'c, C> = IntoGuarded<'c, BoxedFuture<'c, Result<Statement, Error>>, C>;

pub struct IntoGuarded<'a, F, C> {
fut: F,
cli: &'a C,
}

impl<'a, F, C> Future for IntoGuarded<'a, F, C>
where
F: Future<Output = Result<Statement, Error>> + Unpin,
C: Prepare,
{
type Output = Result<StatementGuarded<'a, C>, Error>;

fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
let this = self.get_mut();
Pin::new(&mut this.fut)
.poll(cx)
.map_ok(|stmt| stmt.into_guarded(this.cli))
}
}

impl<'c, 's, C> Execute<'c, C> for StatementNamed<'s>
where
C: Query + Prepare + 'c,
's: 'c,
{
type ExecuteFuture =
ResultFuture<Pin<Box<dyn Future<Output = Result<StatementGuarded<'c, C>, Error>> + Send + 'c>>>;
type ExecuteFuture = ResultFuture<IntoGuardedFuture<'c, C>>;
type RowStream = Self::ExecuteFuture;

#[inline]
fn execute(self, cli: &'c C) -> Self::ExecuteFuture {
cli._query(StatementCreate::from((self, cli)))
.map(|fut| Box::pin(async { fut.await.map(|stmt| stmt.into_guarded(cli)) }) as _)
.map(|fut| IntoGuarded { fut, cli })
.into()
}

Expand Down

0 comments on commit a9fd5e8

Please sign in to comment.