-
Notifications
You must be signed in to change notification settings - Fork 1
Add Query type #4
Conversation
This commit adds a Query type that allows you combine WHERE expressions with some type checks and generate a postgres query. An `all()` method is added to the generated table types for conveniently building a `SELECT` for all table columns and returning the results in a `Vec`.
| let _where1 = Where::gt(Accounts::USER_ID, 1_i32); | ||
| let _where = Where::and(_where1, Where::eq(Accounts::USERNAME, "user4".to_string())); | ||
| let q = Query::new( | ||
| false, | ||
| vec![ | ||
| "user_id".into(), | ||
| "username".into(), | ||
| "password".into(), | ||
| "email".into(), | ||
| ], | ||
| "accounts".into(), | ||
| Some(_where), | ||
| ); | ||
| println!("query to string: {}", q.to_string()); |
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.
Here's what I was really hoping for this to look like:
Accounts::query()
.where(|a| Sql::gt(a.user_id, 1) & Sql::eq(a.username, "user4"))
.select(|a| (a.user_id, a.username, a.password, a.email))Otherwise there's still a lot of string values going on, which can't be type checked.
Do you think an interface like that would be hard to add?
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.
Hm looks possible, I will give it a go!
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 can't figure out a way to type check fields without instantiating the queried type, it's somewhat of blocking progress on this. It might be only possible to do it with a "mirror" struct like here ("Type-checked keypaths in Rust") but that will probably increase the size and complexity of the generated code† so I don't know if I should venture into this road without your feedback first.
† Also, will require proc macros to refer to fields but actually calling the mirror struct methods.
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.
Can you give an example of where you run into trouble?
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.
Typechecking the closure |a| Sql::gt(a.user_id, 1) & Sql::eq(a.username, "user4") is straightforward I guess. But to get the expression out of it you need to pass an instance of the type. It's not trouble per se but a bit unclean. What do you think?
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 had this implementation in mendes::models where the Model impl has an associated type where the fields are column types with a parameter for the value type, I think something like that might help here?
In general I'm not worried about generating too much code, although we should try to avoid too much duplication.
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 had this implementation in mendes::models where the Model impl has an associated type where the fields are column types with a parameter for the value type, I think something like that might help here?
The problem is matching field names and their types, since you cannot inspect fields by their name without using the dot operator on an actual type instance. That's why the link I provided implements a mirror type that replaces fields with method calls, which don't require an instance.
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.
Maybe my previous comment wasn't quite clear, see here:
https://gist.github.com/djc/7eac20dcfb20648a9e00b2ca3152a940#file-models-rs-L121
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.
Ah that makes sense, I didn't find this part by looking at the models module. This is exactly what I was suggesting by "implementing a mirror type". So we can move forward with this solution
This commit adds a Query type that allows you combine WHERE expressions
with some type checks and generate a postgres query. An
all()methodis added to the generated table types for conveniently building a
SELECTfor all table columns and returning the results in aVec.The
select.rsmodule file is self-contained and must be included in generated code.To see generated code run the
basictest withconst TMP_DIR: Option<&'static str> = Some("/tmp/basic/");or some other directory in order to persist the generated code to disk.cargo test basic -- --nocapture --ignoredAn example of a generated SQL query:
all()uses aTryFrom<postgres::row::Row>implementation to convert aRowresult into a Rust type by filling all fields.