Finish passing input parameters through to queries#418
Conversation
eb5a529 to
15d0f5d
Compare
15d0f5d to
4955156
Compare
|
I just realized this uses inputs not named in |
|
Oh, and we don't parse |
4955156 to
3297774
Compare
This also adds a test that an `UnboundVariables` error is raised if a variable mentioned in the `:in` clause isn't bound.
|
@ncalexan: part 1 needs some advice from you on how to get things to compile when returning an error; I can't get it to work. |
core/src/lib.rs
Outdated
| &ValueType::Double => values::DB_TYPE_DOUBLE.clone(), | ||
| &ValueType::String => values::DB_TYPE_STRING.clone(), | ||
| &ValueType::Keyword => values::DB_TYPE_KEYWORD.clone(), | ||
| match *self { |
There was a problem hiding this comment.
I did this on the way, but I suspect it should actually be to_edn_value(self) — ValueType is Copy, so avoiding references wherever possible is best.
query-algebrizer/src/clauses/mod.rs
Outdated
| None => ConjoiningClauses::with_alias_counter(alias_counter), | ||
| Some(QueryInputs { mut types, mut values }) => { | ||
| println!("Got inputs {:?}", values); | ||
| println!("Got input vars {:?}", in_variables); |
There was a problem hiding this comment.
I'll remove these locally.
We also at this point switch from using `Vec<Variable>` to `BTreeSet<Variable>`. This allows us to guarantee no duplicates later; we'll reject duplicates at parse time.
This is for two reasons. Firstly, we need to track the types of inputs, their values, and also the input variables; adding a struct gives us a little more clarity. Secondly, when we come to implement prepared statements, we'll be algebrizing queries without having the values available. We'll be able to do a better job of algebrizing, and also do more validating, if we allow callers to specify the types of variables in advance, even if the values aren't known.
We'll use this to drop unneeded values from input maps, if lazy callers reuse a general-purpose map for multiple queries.
This also adds a test that an `UnboundVariables` error is raised if a variable mentioned in the `:in` clause isn't bound.
3297774 to
dbbfdb2
Compare
…; use combine::primitive::Error.
ncalexan
left a comment
There was a problem hiding this comment.
Few nits, some small requests. Rock on!
| } | ||
|
|
||
| errors { | ||
| DuplicateVariableError { |
There was a problem hiding this comment.
Worth decorating with the variable, I think. (I see your TODO below.)
There was a problem hiding this comment.
Unfortunately it's a little tricky with the way I've implemented it; I hope this can be part of an ergonomics pass in a few months. It should be pretty obvious to users when there are duplicated variables.
| pub struct Order(pub Direction, pub Variable); // Future: Element instead of Variable? | ||
|
|
||
| #[derive(Clone, Debug, Eq, PartialEq)] | ||
| #[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord)] |
There was a problem hiding this comment.
nit: prefer alphabetical order.
There was a problem hiding this comment.
This was copypasta'd from elsewhere, but sure.
| Variable, | ||
| }; | ||
|
|
||
| pub use errors::{ |
There was a problem hiding this comment.
Oh, I get your question. Yeah, probably unnecessary.
| /// When built correctly, `types` is guaranteed to contain the types of `values` -- use | ||
| /// `QueryInputs::new` or `QueryInputs::with_values` to construct an instance. | ||
| pub struct QueryInputs { | ||
| pub types: BTreeMap<Variable, ValueType>, |
There was a problem hiding this comment.
Do these need to be pub? You could make a stronger statement if they were hidden, and only exposed via accessors.
There was a problem hiding this comment.
I'd like them not to be, but there's no concept of crate-private in Rust. I could flatten inputs.rs into mod.rs…
|
|
||
| impl Default for QueryInputs { | ||
| fn default() -> Self { | ||
| QueryInputs { types: BTreeMap::default(), values: BTreeMap::default() } |
There was a problem hiding this comment.
Didn't we settle on
Name {
x: X,
y: Y,
...
}(Here and below.)
| /// Remove all keys from the map that are not present in `ks`. | ||
| /// This implementation is terrible because there's no mutable iterator for BTreeMap. | ||
| fn keep_intersected_keys(&mut self, ks: &BTreeSet<K>) { | ||
| let mut to_remove = Vec::with_capacity(self.len() - ks.len()); |
There was a problem hiding this comment.
I expect this to panic when self.len() is less than ks.len(). That might be okay, but can you be safe about it?
|
|
||
| trait Intersection<K> { | ||
| fn with_intersected_keys(&self, ks: &BTreeSet<K>) -> Self; | ||
| fn keep_intersected_keys(&mut self, ks: &BTreeSet<K>); |
There was a problem hiding this comment.
You have one mutating and one non-mutating method. Is there a compelling reason to have different API regimes?
There was a problem hiding this comment.
Well, it means we can write:
value_bindings: self.value_bindings.with_intersected_keys(&vars),
rather than
let new_value_bindings = self.value_bindings.clone();
new_value_bindings.keep_intersected_keys(&vars);
…
value_bindings: new_value_bindings,
The reason for k_i_k is because we're already taking ownership of the inputs, so it's wasteful to clone into a new map.
| match inputs.into() { | ||
| None => ConjoiningClauses::with_alias_counter(alias_counter), | ||
| Some(QueryInputs { mut types, mut values }) => { | ||
| // Discard any bindings not mentioned in our :in clause. |
There was a problem hiding this comment.
Do we test somewhere that all :in variables appear? I feel like Datomic probably doesn't care, but we should probably fail in this case, since it's likely to be an error.
There was a problem hiding this comment.
We do check that they're all bound. I don't think we check that they're a subset of the variables in the query. I'll file.
|
|
||
| errors { | ||
| UnboundVariables(names: BTreeSet<String>) { | ||
| description("unbound variables at execution time") |
There was a problem hiding this comment.
nit: "query execution time"? There are many things being executed at many times.
| // Because this is q_once, we can check that all of our `:in` variables are bound at this point. | ||
| // If they aren't, the user has made an error -- perhaps writing the wrong variable in `:in`, or | ||
| // not binding in the `QueryInput`. | ||
| let unbound = algebrized.unbound_variables(); |
There was a problem hiding this comment.
Ah! I think this closes an open loop from earlier.
There was a problem hiding this comment.
I did this because I wrote a test that failed because I typoed the :in variable :)
This also adds a test that an `UnboundVariables` error is raised if a variable mentioned in the `:in` clause isn't bound.
This commit downgrades error_chain to 0.8.1 in order to fix trait bounds on errors.
|
Thanks for the help, Nick! |
|
Review comments addressed in bffefe7. |
In the course of starting another feature, I realized this wasn't finished.