Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support concatenation in query*! macros #388

Closed
NyxCode opened this issue Jun 7, 2020 · 3 comments
Closed

Support concatenation in query*! macros #388

NyxCode opened this issue Jun 7, 2020 · 3 comments

Comments

@NyxCode
Copy link

NyxCode commented Jun 7, 2020

I would like to use macros to generate calls to query!, e.g:

macro_rules! get_by_id {
    ($s:ident, $t:literal) => {
        impl $s {
            async fn get_by_id(db: &mut PgConnection, id: i32) -> Result<Self> {
                sqlx::query_as!(Self, "SELECT * FROM " + $t + " WHERE id = $1", id)
                    .fetch_one(db)
                    .await
            }
        }
    }
}

struct User {
    id: i32,
    name: String
}
get_by_id!(User, "registered_users");

For this, string concatination in the query_as! macro is necessarry. While the rust stdlib supports macro calls inside println! (playground), @abonander suggested using + for string concatenation, like in the example above.

I believe this would tremendously reduce boilerplate code for many users, while still having all queries checked at compile time.

@mehcode
Copy link
Member

mehcode commented Jun 7, 2020

I don't dislike + but I think we should also support concat! / stringify! like println!. No real need to be scared of SQL injection as its all compile-time anyway.

println!(concat!("Hello, ", stringify!(10)));

@abonander
Copy link
Collaborator

There's a certain logical extreme where if we support concat!() and stringify!() then maybe people come to expect us to support include_str!() as well, which we currently can't reproduce on stable (#11); I think that might end up surprising people.

@mikeplus64
Copy link
Contributor

mikeplus64 commented Jul 2, 2020

Rust newbie here; I thought I could have a go at implementing this by patching query{_as,_as_unchecked,_unchecked}! to accept a tt for the query parameter, then patching QueryMacroInput::parse as in ...

            if key == "source" {
                enum SourceFragment { Str(LitStr), Ident(Ident) };
                let mut fragments: Vec<SourceFragment> = Vec::new();
                'source_parser: while !input.is_empty() {
                    fragments.push(match input.lookahead1() {
                        la if la.peek(Ident) => SourceFragment::Ident(input.parse()?),
                        la if la.peek(LitStr) => SourceFragment::LitStr(input.parse()?),
                        _ => break 'source_parser,
                    });
                    if !input.lookahead1().peek(Token![+]) {
                        break 'source_parser;
                    }
                }
                if fragments.len() == 0 {
                    return Err(syn::Error::new_spanned(key, "no source given"));
                }
                query_src = ???;

But, as you can see, I have no idea how to "unquote" the fragments vec into a concrete String, and fear this approach is simply impossible. In fact, I have no idea how println! can do it either =D... Eventually a single String must be available (at compile-time) in order to do compile-time checking against the DB.

Maybe https://github.com/Emoun/eager can be used to make this a bit easier?

Edit: I realised what I thought was necessary was not, really, since just supporting Vec<LitStr> is enough to at least make queries parameterisable by an outer macro_rules as in the issue. Made a PR here #474

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

4 participants