- Notifications
You must be signed in to change notification settings - Fork 1.5k
Added queryx macro with support for attributes on inferred type #4115
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
base: main
Are you sure you want to change the base?
Conversation
| The intention has always been that if you want control over the generated structure, you should be using This is especially true if you're wanting to implement If you just wanted to add What would be cool is to have some utility that can automatically convert any I also don't agree with trying to combine the functionality of all the I am actually wanting to get rid of some variants, but that's by making the macros more composable with existing mechanisms that the user should already be familiar with (in fact, a lot of people expect this to work already and get frustrated that it doesn't, which is understandable). Having to list column names twice (or more) is annoying, but I want to address that in other ways:
|
I'm not convinced by the claim that it's more maintainable. The more code there is, the more code there is to maintain, and the more duplication there is, the more things there are that need to match.
Is it really that much harder to mentally convert from the SQL columns than to mentally convert from the fields of a Rust struct.
True, but this could equally well be solved by adding more features to the macro, which is more practical when it is one macro.
Actually, the code that always derives I figured if this was best for the
I hadn't thought about this and I like this idea, but it doesn't change my opinion on
The motivation for combining If there is such resistance to the DSL part though, it might be better to include the same functionality by adding optional keyword arguments by exposing a thinner wrapper over
I've done some testing on this with Rust Analyzer (I don't have easy access to other setups). I think the main place you would want and expect autocompletion is in the parameters, and Rust Analyzer seems to understand those as expression position, and stops offering hints if you try and pass too many parameters. This is the same for both.
I'd be happy to use
This is great news which I hadn't been following. I don't think it completely solves the problem though because plenty of people (including myself) will almost always write out all the columns in their query. So if you want to derive other traits you still need to write it out twice.
I'd love to see this feature, but the old behavior should probably still be available because it generates less code. So... even more macros?
This would be great. AutocompletionSince these macros already generate a closure, why not make the closure the output of the macro so that it emits a function that you call with the macro parameters? This seems like it should be possible and then you could even get signature help on the parameters. It should probably call the closure automatically if there are no parameters. ConclusionThere are clearly multiple features that would be good to add to these macros. It's not practical to have a separate variant for each combination --- I think you agree with me on this. Therefore there will have to be some macros that accommodate multiple combinations. You could still divide the combinations into multiple macros, but I think you might as well have an everything macro. Your points about the syntax being harder to manage are completely valid, and I'm open to switching to a keyword based syntax. This is probably also more resilient against needing changes to existing code to accommodate the syntax for new features. I would say though that there should still be some syntax, even if most features are done using keywords, because it is desirable to still do very simple things with very simple syntax. |
Except in larger applications you often end up writing multiple queries that return the same structure, anyway. And sometimes you need some post-processing steps to fix up the structure. Having control over the structure's definition is so much more valuable than I think you realize. Ctrl-F I realized that if we implemented #3388, then it'd be very easy to create reusable query fragments for less duplication, e.g.: macro_rules! select_from_foo( ($tail:literal) => { concat!("SELECT bar, baz, quux FROM foo ", $tail) } ); sqlx::query_as!(Foo, select_from_foo!("WHERE bar = $1"), bar)We could create a macro to make this even easier: sqlx::query_fragments! { select_from_foo => "SELECT bar, baz, quux FROM foo", select_from_foo_join_bar => "SELECT bar.*, baz, quux FROM foo INNER JOIN bar USING (bar_id)", } sqlx::query_as!(Foo, select_from_foo_join_bar!("WHERE bar_id = $1"), bar_id)That doesn't necessarily solve the problem of writing the columns twice, but it saves you having to write them out more than that, which is definitely something.
There's actually a double indirection there. To understand what the JSON output should look like, you'd have to understand what the Rust struct should look like, and to understand what the Rust struct should look like, you'd have to parse the SQL to see what columns are being returned. That's a ton of cognitive overhead. And then there's even more temptation to be really lazy and just do Experience tells me this feature would just be a massive footgun, and I'm really not comfortable with that.
I honestly forgot we had that, and I didn't notice it in the screenshot because it ended up on the previous line for some reason. Maybe an artifact of how we concatenate token streams?
One big problem is that it's much, much less practical to document, and to teach. The docs for You didn't really run into this because you just link to the docs for all the other macros: https://github.com/launchbadge/sqlx/pull/4115/files#diff-5ee96057bd109a82372c87c64ed6c0b59a16257e72311971b642523a3c68dd6fR875-R885 The separate macros provide a very natural, easily navigable boundary between doc sections, and it plays much better with how rustdoc works, especially searching.
The whole point has been that if you need more control than that, you should really be using This is not all that different from how Rust allows the types of local variables to be inferred but requires function signatures to be fully specified. While yes, a significant motivation was implementation complexity, it also turns out that having abstractions with well defined interfaces is extremely important for maintainable software.
I have no intention of adding more variants. Like I said, I'm trying to get rid of some of them. As you've seen, 90% of the complexity isn't in having multiple macros anyways. #514 wouldn't be a new operating mode; it'd be a breaking change to And at the end of the day, "too magical" is really the issue here. I don't want these macros to become a magical black box where you put SQL in and get JSON out, and the fact that it's mapped to a Rust struct is just an implementation detail. I've really come to despise systems like that, because when things go wrong you have very little capability to introspect the system and try to understand what's gone wrong. It also makes it a nightmare to find your way around a new project. You have to learn how the black box works to have any idea what's going on. The further a black box's effects can reach, the more lost you are until you figure it out. With With this, you kind of have... the worst of both worlds. |

The syntax for
queryx!is as described in #4114.Does your PR solve an issue?
fixes #4114
fixes #4113
Is this a breaking change?
This is not a breaking change, existing macros should behave exactly the same as before.
TODO
queryx!queryx!(currently bad syntax usually results in macro expansion overflow)