-
Notifications
You must be signed in to change notification settings - Fork 40
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
[db-queries] Decouples CTE usage from Diesel (RegionAllocation) #5063
Conversation
/// If arbitrary user input is provided here, this string COULD | ||
/// cause SQL injection attacks, so each call-site should have a | ||
/// justification for "why it's safe". | ||
pub fn this_string_wont_cause_sql_injections(s: String) -> Self { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
“this string could” reads like more of a warning that “this string won’t”, which could be misinterpreted as a reassurance
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Updated to i_take_responsibility_for_validating_this_string
- I'm okay wordsmithing this, since IMO it should be long, obvious, and hard to accidentally call.
Also wrapped this in a struct so a caller can't create the enum. I always forget that all enum variants are always accessible for construction.
Weirdly this PR seems to be causing failures on the integration tests, but not on the more "targeted to the query" tests in As a side-note: It's been kinda useful to use |
I think I found the underlying issue breaking the tests. https://docs.rs/diesel/latest/diesel/dsl/fn.sql_query.html relies on Diesel's Basically: It returns a I really wish there was a way to have columns queried by Diesel returned to a struct in the order they were parsed. I don't know why |
/// If arbitrary user input is provided here, this string COULD | ||
/// cause SQL injection attacks, so each call-site should have a | ||
/// justification for "why it's safe". | ||
pub fn i_take_responsibility_for_validating_this_string(s: String) -> Self { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
this is wonderful :)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I hope it's the right direction, and I'd be stoked to move things more and more into "const fn"s as we can.
I think the choices made by Diesel (e.g. https://docs.diesel.rs/master/diesel/query_builder/struct.SqlQuery.html#method.sql, which just takes anything that impls AsRef<str>
) makes this a little too easy to foot-gun via SQL injection when building queries that side-step the norms of Diesel's type system.
I know it's possible to subvert this with &'static str
too, and I wish there was a way to more explicitly say "compile-time-constructed string", but that's as close a proxy as I think I can get right now.
so_that_was_a_lie.jpeg Turns out you can use the newtype pattern, re-implement some Diesel traits (https://docs.diesel.rs/master/diesel/prelude/trait.RunQueryDsl.html to indicate that it's executable, https://docs.diesel.rs/master/diesel/query_builder/trait.Query.html , with specific SQL type to say "what the output should be deserialized from", and https://docs.diesel.rs/master/diesel/query_builder/trait.QueryFragment.html to plumb through the underlying This seems to work. I'm using a new type named |
My two cents: I really hope I'm not causing us to move away from what Diesel gives us. The entire time I had where things wouldn't compile was due to me missing a join on a table that I later used in a WHERE filter: this was a legit error that diesel was flagging. Once that was cleared up, everything worked. I much prefer working with the older CTE, though I do love the other things this PR adds.
Even though I had a frustrating time trying to figure out what the problem was, I'd still argue that Diesel gives us more than just raw SQL. It's especially useful if we need to refactor or reshape things so we can share code, etc. |
This is definitely not just you -- I can clarify the PR description? But I have heard from many people interfacing with CTEs and Diesel and struggling (@bnaecker , @jgallagher , @internet-diglett , and more) because the workflow is "figure out what SQL to write", and then "figure out how to convince Diesel to accept it". If translating SQL to Diesel bought us guarantees that we could not get any other way, I'd be more convinced, but for more complex queries like this, I think we can get sufficient protection -- and a flatter structure -- by using something much closer to raw SQL.
To expand a little bit on the value-proposition: When I originally added the CTE utilities, I had the same hopes. I wanted to use Diesel's type-safety guarantees to help us ensure things like:
I still think these are all valid protections, that we should ensure! I also think that the EXPLAIN-based tests, in addition to very simple integration tests, provide protection against all these things. Furthermore, they do so with fewer types, without any need for aliasing, without any need for JOIN-based macros, and immediately with support for arbitrary Postgres / CockroachDB support that may or may not be represented in Diesel. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looks good to me!
This is a direct follow-up to #5063, focused on the `virtual_provisioning_collection` CTE. This PR adds a test which validates the current SQL query, before re-structuring it to use `TypedSqlQuery`.
Diesel's complex type have been a pain point in the region allocation CTE in particular:
alias!
calls to allow a table to appear in aSELECT
clause in multiple spotsThis PR does the following:
raw_query_builder.rs
for a wrapper around Diesel'sSqlQuery
object, to make SQL injections less possible and to track bind parameter counting.RegionAllocation
CTE to use this builder, interleaved with raw SQL.AllColumnsOf<Table>::with_prefix(&'static str)
, to enumerate all the columns of a table as strings.EXPLAIN
test to the CTE, to quickly validate that CockroachDB thinks it's producing valid output.Here are my thoughts for future improvements:
EXPLAIN
tests) to ask CockroachDB to validate type information for us.