mirror of
https://github.com/launchbadge/sqlx
synced 2024-11-10 14:34:19 +00:00
Add query_as_unchecked! and query_file_as_unchecked! to use the macro system with unchecked input and output
This commit is contained in:
parent
a2d82d0ac1
commit
2f80621279
8 changed files with 104 additions and 9 deletions
|
@ -141,13 +141,26 @@ pub fn query_file(input: TokenStream) -> TokenStream {
|
|||
#[allow(unused_variables)]
|
||||
pub fn query_as(input: TokenStream) -> TokenStream {
|
||||
#[allow(unused_variables)]
|
||||
async_macro!(db, input: QueryAsMacroInput => expand_query_as(input, db))
|
||||
async_macro!(db, input: QueryAsMacroInput => expand_query_as(input, db, true))
|
||||
}
|
||||
|
||||
#[proc_macro]
|
||||
#[allow(unused_variables)]
|
||||
pub fn query_file_as(input: TokenStream) -> TokenStream {
|
||||
async_macro!(db, input: QueryAsMacroInput => expand_query_file_as(input, db))
|
||||
async_macro!(db, input: QueryAsMacroInput => expand_query_file_as(input, db, true))
|
||||
}
|
||||
|
||||
#[proc_macro]
|
||||
#[allow(unused_variables)]
|
||||
pub fn query_as_unchecked(input: TokenStream) -> TokenStream {
|
||||
#[allow(unused_variables)]
|
||||
async_macro!(db, input: QueryAsMacroInput => expand_query_as(input, db, false))
|
||||
}
|
||||
|
||||
#[proc_macro]
|
||||
#[allow(unused_variables)]
|
||||
pub fn query_file_as_unchecked(input: TokenStream) -> TokenStream {
|
||||
async_macro!(db, input: QueryAsMacroInput => expand_query_file_as(input, db, false))
|
||||
}
|
||||
|
||||
#[proc_macro_derive(Encode, attributes(sqlx))]
|
||||
|
|
|
@ -13,6 +13,7 @@ use crate::query_macros::QueryMacroInput;
|
|||
pub fn quote_args<DB: DatabaseExt>(
|
||||
input: &QueryMacroInput,
|
||||
describe: &Describe<DB>,
|
||||
checked: bool,
|
||||
) -> crate::Result<TokenStream> {
|
||||
let db_path = DB::db_path();
|
||||
|
||||
|
@ -24,7 +25,7 @@ pub fn quote_args<DB: DatabaseExt>(
|
|||
|
||||
let arg_name = &input.arg_names;
|
||||
|
||||
let args_check = if DB::PARAM_CHECKING == ParamChecking::Strong {
|
||||
let args_check = if checked && DB::PARAM_CHECKING == ParamChecking::Strong {
|
||||
describe
|
||||
.param_types
|
||||
.iter()
|
||||
|
|
|
@ -30,6 +30,7 @@ where
|
|||
pub async fn expand_query_as<C: Connection>(
|
||||
input: QueryAsMacroInput,
|
||||
mut conn: C,
|
||||
checked: bool,
|
||||
) -> crate::Result<TokenStream>
|
||||
where
|
||||
C::Database: DatabaseExt + Sized,
|
||||
|
@ -45,7 +46,7 @@ where
|
|||
.into());
|
||||
}
|
||||
|
||||
let args_tokens = args::quote_args(&input.query_input, &describe)?;
|
||||
let args_tokens = args::quote_args(&input.query_input, &describe, checked)?;
|
||||
|
||||
let query_args = format_ident!("query_args");
|
||||
|
||||
|
@ -55,6 +56,7 @@ where
|
|||
&input.as_ty.path,
|
||||
&query_args,
|
||||
&columns,
|
||||
checked,
|
||||
);
|
||||
|
||||
let arg_names = &input.query_input.arg_names;
|
||||
|
@ -75,10 +77,11 @@ where
|
|||
pub async fn expand_query_file_as<C: Connection>(
|
||||
input: QueryAsMacroInput,
|
||||
conn: C,
|
||||
checked: bool,
|
||||
) -> crate::Result<TokenStream>
|
||||
where
|
||||
C::Database: DatabaseExt + Sized,
|
||||
<C::Database as Database>::TypeInfo: Display,
|
||||
{
|
||||
expand_query_as(input.expand_file_src().await?, conn).await
|
||||
expand_query_as(input.expand_file_src().await?, conn, checked).await
|
||||
}
|
||||
|
|
|
@ -101,6 +101,7 @@ pub fn quote_query_as<DB: DatabaseExt>(
|
|||
out_ty: &Path,
|
||||
bind_args: &Ident,
|
||||
columns: &[RustColumn],
|
||||
checked: bool,
|
||||
) -> TokenStream {
|
||||
let instantiations = columns.iter().enumerate().map(
|
||||
|(
|
||||
|
@ -110,7 +111,16 @@ pub fn quote_query_as<DB: DatabaseExt>(
|
|||
ref type_,
|
||||
..
|
||||
},
|
||||
)| { quote!( #ident: row.try_get::<#type_, _>(#i).try_unwrap_optional()? ) },
|
||||
)| {
|
||||
// For "checked" queries, the macro checks these at compile time and using "try_get"
|
||||
// would also perform pointless runtime checks
|
||||
|
||||
if checked {
|
||||
quote!( #ident: row.try_get_unchecked::<#type_, _>(#i).try_unwrap_optional()? )
|
||||
} else {
|
||||
quote!( #ident: row.try_get_unchecked(#i).try_unwrap_optional()? )
|
||||
}
|
||||
},
|
||||
);
|
||||
|
||||
let db_path = DB::db_path();
|
||||
|
|
|
@ -23,7 +23,7 @@ where
|
|||
let describe = input.describe_validate(&mut conn).await?;
|
||||
let sql = &input.source;
|
||||
|
||||
let args = args::quote_args(&input, &describe)?;
|
||||
let args = args::quote_args(&input, &describe, true)?;
|
||||
|
||||
let arg_names = &input.arg_names;
|
||||
let db_path = <C::Database as DatabaseExt>::db_path();
|
||||
|
@ -57,7 +57,8 @@ where
|
|||
.collect::<TokenStream>();
|
||||
|
||||
let query_args = format_ident!("query_args");
|
||||
let output = output::quote_query_as::<C::Database>(sql, &record_type, &query_args, &columns);
|
||||
let output =
|
||||
output::quote_query_as::<C::Database>(sql, &record_type, &query_args, &columns, true);
|
||||
|
||||
Ok(quote! {
|
||||
macro_rules! macro_result {
|
||||
|
|
|
@ -284,3 +284,48 @@ macro_rules! query_file_as (
|
|||
macro_result!($($args),*)
|
||||
})
|
||||
);
|
||||
|
||||
/// A variant of [query_as!] which does not check the input or output types. This still does parse
|
||||
/// the query to ensure it's syntactically and semantically valid for the current database.
|
||||
#[macro_export]
|
||||
#[cfg_attr(docsrs, doc(cfg(feature = "macros")))]
|
||||
macro_rules! query_as_unchecked (
|
||||
($out_struct:path, $query:literal) => (#[allow(dead_code)] {
|
||||
#[macro_use]
|
||||
mod _macro_result {
|
||||
$crate::sqlx_macros::query_as_unchecked!($out_struct, $query);
|
||||
}
|
||||
macro_result!()
|
||||
});
|
||||
|
||||
($out_struct:path, $query:literal, $($args:expr),*$(,)?) => (#[allow(dead_code)] {
|
||||
#[macro_use]
|
||||
mod _macro_result {
|
||||
$crate::sqlx_macros::query_as_unchecked!($out_struct, $query, $($args),*);
|
||||
}
|
||||
macro_result!($($args),*)
|
||||
})
|
||||
);
|
||||
|
||||
/// A variant of [query_file_as!] which does not check the input or output types. This
|
||||
/// still does parse the query to ensure it's syntactically and semantically valid
|
||||
/// for the current database.
|
||||
#[macro_export]
|
||||
#[cfg_attr(docsrs, doc(cfg(feature = "macros")))]
|
||||
macro_rules! query_file_as_unchecked (
|
||||
($out_struct:path, $query:literal) => (#[allow(dead_code)] {
|
||||
#[macro_use]
|
||||
mod _macro_result {
|
||||
$crate::sqlx_macros::query_file_as_unchecked!($out_struct, $query);
|
||||
}
|
||||
macro_result!()
|
||||
});
|
||||
|
||||
($out_struct:path, $query:literal, $($args:tt),*$(,)?) => (#[allow(dead_code)] {
|
||||
#[macro_use]
|
||||
mod _macro_result {
|
||||
$crate::sqlx_macros::query_file_as_unchecked!($out_struct, $query, $($args),*);
|
||||
}
|
||||
macro_result!($($args),*)
|
||||
})
|
||||
);
|
||||
|
|
|
@ -59,6 +59,29 @@ async fn test_query_as_raw() -> anyhow::Result<()> {
|
|||
Ok(())
|
||||
}
|
||||
|
||||
#[cfg_attr(feature = "runtime-async-std", async_std::test)]
|
||||
#[cfg_attr(feature = "runtime-tokio", tokio::test)]
|
||||
async fn test_query_as_bool() -> anyhow::Result<()> {
|
||||
let mut conn = new::<MySql>().await?;
|
||||
|
||||
struct Article {
|
||||
id: i32,
|
||||
deleted: bool,
|
||||
}
|
||||
|
||||
let article = sqlx::query_as_unchecked!(
|
||||
Article,
|
||||
"select * from (select 51 as id, true as deleted) articles"
|
||||
)
|
||||
.fetch_one(&mut conn)
|
||||
.await?;
|
||||
|
||||
assert_eq!(51, article.id);
|
||||
assert_eq!(true, article.deleted);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[cfg_attr(feature = "runtime-async-std", async_std::test)]
|
||||
#[cfg_attr(feature = "runtime-tokio", tokio::test)]
|
||||
async fn test_query_bytes() -> anyhow::Result<()> {
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
use futures::TryStreamExt;
|
||||
use sqlx::error::DatabaseError;
|
||||
use sqlx::{mysql::MySqlQueryAs, Connection, Executor, MySql, MySqlPool};
|
||||
use sqlx_test::new;
|
||||
use std::time::Duration;
|
||||
|
|
Loading…
Reference in a new issue