-
-
Notifications
You must be signed in to change notification settings - Fork 1.1k
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
allow_tables_to_appear_in_same_query! does not scale with larger number of tables #4333
Comments
Thanks for opening this issue. I fear there is nothing much we can do here as that's a fundamental check required for how diesel checks if your queries are valid. At least I'm not aware of a better way to model this in the rust type system. Technically speaking we could possibly offer an option to not generate this macro call, which would have the downside that you would need to write these call for all the relevant tables on your own, which quickly can become complicated and will result in confusing error messages if you miss that. As for the rustc compile time regression: That's something you should report to the rustc developers as that's their regression. They are usually quite sensitive to such reports, so if you show up with a reproducible report that something got significantly worse I'm quite sure that there will be a response there. |
It looks like they're already working on a fix for the regression here: rust-lang/rust#132075 To make sure I understand, the check is there to make sure the queries are against tables/aliases within the same database, right? |
That's not correct. This trait is used to count how often a table appears in the from clause of your query. That in turn is used to check that all the relevant tables are there and that each table appears only once in a not-aliased variant. |
I see. I've been looking into alternatives, because even when the upstream issue is addressed we're throwing a LOT of individually generated traits at the compiler. If we had some sort of internal trait that exposed a
and it should allow compile-time assertions that T1 != T2. Then it's just a matter of setting up I also looked at using something like typewit and |
I fear that won't work as you currently cannot declare const functions in traits in stable rust. Even if that's somehow possible you then cannot use the proposed check in the relevant functions as rust disallows using generic parameters from outer items there: https://play.rust-lang.org/?version=stable&mode=debug&edition=2021&gist=57f94e490577068c5e7f49452718ec11
I'm open to accept specific implementations, as long as they don't break the existing public API and can demonstrate that they improve the compile time for something. I likely won't have the capacity to spend much time on that myself. |
You're right that you can't declare a const fn, but you can do something like this in a trait as long as you have some statically accessible
While playing around I'm using the table name here (
If you want to do this sort of test within the trait impl, you simply need to make sure there's a const available to override within the impl.
So in this case you would override the const in the impl wherever you want to perform a check. If you try to instantiate the generic impl within a context where the check would fail, the assertion fails and you get a compiler error with the message. Implementing the trait without specifying a check is also fine, it'll just do no checks. The main issue I'm running into right now is conflicting impl definitions after removing the The end solution may require removing the Peano numbers entirely, but if we can do these negative checks at each I think you'll find they actually aren't needed. In short, if a table doesn't appear we simply don't care-- no need to generate any trait. If it does appear as the first FROM element (by itself), we can generate For composite cases this obviously requires us to be more clever with the The best thing about this approach is it would avoid the cost of generating a bunch of traits for non-behavior. We wouldn't need a trait for "this table might appear in this query but hasn't yet". |
I must admit that I still do not see how you can go from this:
to something that actually uses these constants here: diesel/diesel/src/query_source/mod.rs Lines 154 to 160 in 2090344
or in any other place this check needs to happen. The main problem there is that I think the other issues around conflicting impls might actually be solvable as we already have certain patterns to workaround that (see the second generic paramater of |
Alright, so assume for the moment that we're writing some code that has to perform an inner join across two tables (via a known foreign key) and then I'm still honestly trying to wrap my head around why we need Peano numbers here, and how the traits all interlock. I was trying to remove the table equals constraint and rebuild it from scratch, but I kept running into trait issues or running out of stack. Can you help me understand why we need to explicitly keep track of how many times a table appears? It still seems like we should be able to perform the uniqueness check at each point where a table is added to an existing query string and get rid of Peano counting altogether. It appears now we have a It's a bit of a mental stretch, and please forgive me being loose with the syntax. But imagine if you have an existing
so let's just refer to that type as
Now try to |
The Peano numbers are used to count how often a table appears in a from clause. That boils down to the following three variants in the end:
Only the
This is all correct and fine, but I still fail to see how you would be able to write that const match + panic in a generic trait implementation by referring to the outer generic parameters. That's the issue I would like to see solved first before talking about any other details, because if we cannot solve that this approach just won't work in the end. |
I'm saying avoid the inner trait entirely. Don't track the "Never" or "MoreThanOnce" cases at all. If it's "Never" just don't generate a trait. If it would be "MoreThanOnce" then const panic instead. |
That's all fine and good but again I still cannot see how you would implement that const panic thing. At some point you must use generics from some outer trait for this and that's something that's not supported by rust. Before discussing all these details I would really like to see an solution to that problem first, because otherwise all those details do not matter because it's impossible to implement. |
Adding more tables increases the number of generated
TableNotEqual
trait impls by N squared, since theallow_tables_to_appear_in_same_query
macro generates pairwise trait impls. Past about 10-15 tables this N squared codegen has a significant impact on compile time.Making this more difficult, there's no way to prevent the autogenerated
allow_tables_to_appear_in_same_query
block except via patch file. We already have an existing patch file to deal with another schema issue, and it doesn't look like there is a way to specify multiple files indiesel.toml
.I'm not sure how to fix this, aside from completely reworking how table checks work. I recognize it's important as part of the type system to not generate queries spanning two disjoint databases, but this is a problem many if not most users won't encounter. Perhaps there's a more scalable way to do the same check with an O(1) trait generation like
BelongsToDatabase<T>
and some clever bounds checking.Setup
Versions
Problem Description
We started noticing this more as we've added more tables, and it got significantly worse after changes to the coherence checker in Rust 1.82.
Compounds with rust-lang/rust#132064
Steps to reproduce
Create a database with a large number of tables, via
diesel cli
and the normal migrations. In our case we have about 70 tables in our database. TheGenerate a library crate with associated autogenerated
schema.rs
, import it and time the build.Nightly isn't required to exhibit the problem, but
RUSTFLAGS=-Ztime-passes cargo +nightly check
shows the majority of time being spent in the coherence checker. This is consistent with a very large number of trait impls (by my estimate, 70 tables with the Postgres backend will generate approximately 50000 trait impls!)Checklist
closed if this is not the case)
The text was updated successfully, but these errors were encountered: