Published client To Do
Before publishing client crate on crates.io we need:
- [x] Ensure that error reporting is adequate and errors can be handled apropriately. Probably get rid of
anyhow::Error. If even if we leaveanyhow, we should ensure that it's easy to find out if error is recoverable, if it's network error, etc. - [ ] Figure out if it makes sense to move query methods into a trait (async traits are kinda complex, though)
- [x] Review list of dependencies, currently there are too much of them
- [x] Also async-std should not require
unstablefeature - [x] Some of them, like
whoamishould be moved under a feature gate
- [x] Also async-std should not require
- [ ] Review public exports, hide unneeded things and restructure modules if needed
- [ ] Move
Sequenceunder a feature gate (e.g.unstable)
- [ ] Move
- [ ] Query arguments refactoring
- [x]
QueryArgstrait - [ ]
QueryArgtrait - [ ]
QueryArgimplementation for all basic types - [ ]
QueryArgsimplementation for tuple and container types - [ ]
derive(QueryArgs)
- [x]
- [x] Create a Datetime struct to represent edgedb's native datetime type #42
- [ ] Implement most of the RFC1004
- [ ] Reconnecting connection
- [ ] Connection pool
- [ ] Transactions
- [ ] Retriable transactions
- [ ] Transaction configuration/Retry options
- [ ] Capabilities
These tasks can be postponed:
- [ ] Look into supporting multiple runtimes #49
- [ ] Ensure that nothing is re-exported from
edgedb-protocol, so that we can make breaking changes in latter without disturbing users- [ ] Figure out what to do with
Queryabletrait
- [ ] Figure out what to do with
- [ ] Take a look if we want to get rid of
snafufor errors (used only internally)
Previous discussion in edgedb/edgedb-cli#112, #30
In the meantime, anyone can use git version at their own risk (API will probably break every now and then)
The query functions taking a single arbitrary Value looks weird as well, since it's always a list of key-value pairs. I'd have expected some kind of map or named tuple there
The query functions taking a single arbitrary
Valuelooks weird as well, since it's always a list of key-value pairs. I'd have expected some kind of map or named tuple there
Well, it has to be either a tuple (i.e. a sequence) or a named tuple (i.e. a mapping) of variables. So accepting only a vector or only a mapping wouldn't work. Also a Vec<Value> is not very different to what we have now. And there aren't too many uses of Vec<T> because arguments are often of different types.
We can probably have a trait. But that trait is probably going to be just alias to Into<Value> (we may want to make more conversions supported, though).
If it's a tuple, how does the query access it? Something like $0 instead of $name? My preference would be using a smaller enum QueryArgs with only two options (tuple, named tuple) instead of Value here.
If it's a tuple, how does the query access it? Something like
$0instead of$name? My preference would be using a smaller enumQueryArgswith only two options (tuple, named tuple) instead ofValuehere.
Yes. You can either write:
SELECT <str>$name
Or:
SELECT <str>$0
Smaller enum might work. But what individual elements are? Still Value?
I'd prefer something more trait based, so it works with static typing and not just Value.
Something along the lines of:
- Introduce types encapsulating various
Valuevariants, likeNamedTupleandTuple - Rename
Queryableto something likeQueryResult(orDeserialize,Decode) a) It's implemented forValue, the relevant primitve types and encapsulatedValuevariants b) It can be derived for structs c) It's implemented for standard tuples if the values are allQueryResult - Introduce a trait
QueryArg(orSerialize,Encode) a) It's implemented forValue, the relevant primitve types and encapsulatedValuevariants b) It can be derived for structs c) It's implemented for standard tuples if the values are allQueryArg - Introduce a trait
QueryArgsa) It's implemented forNamedTupleandTupleb) It can be derived for structs c) It's implemented for standard tuples if the values are allQueryArgd) Add a macro to allow easy construction of aNamedTupleon the fly, e.g.named!(name=name(), password=pw()) - Add a variant to
Valuewhich contains aBox<dyn QueryArg>so you can mix static and dynamic typing.
(Just a very rough idea, I'm sure that there are a bunch of details that don't make sense)
Ensure that nothing is re-exported from edgedb-protocol, so that we can make breaking changes in latter without disturbing users
I don't see how that's going to work. You can hide the message related parts. But edgedb-protocol also defines all the edgedb database types and the traits abstracting over them.
Probably get rid of
anyhow::Error.
I agree. Anyhow is great for applications, like edgedb-cli, but I'd avoid it in libraries like edgedb-client.
Ensure that nothing is re-exported from edgedb-protocol, so that we can make breaking changes in latter without disturbing users
I don't see how that's going to work. You can hide the message related parts. But edgedb-protocol also defines all the edgedb database types and the traits abstracting over them.
Well, yes Value is a problem. But if we go with some sort of your proposal above, we can allow statically typed queries without depending on Value, and statically typed queries are ones I expect to be at least 80% of Rust use cases (Value is mostly for REPL).
Will comment on the proposal of QueryArg above later.
So the whole proposal looks sensible. Except few minor things:
- Not sure about
QueryResultname (especially if #40 fixed) - We don't support tuple arguments yet, so
QueryArgfor tuple is not needed yet -
Box<dyn QueryArg>should be at least postponed. The primary purpose ofValueis for returning value rather than for input. I think dynamic use cases might be covered well enough by using tuple ofBox<dyn QueryArg>or struct wrapping that.
One problem with abstracting over Value and Queryable is context dependent (de-)serialization. Value is context dependent (especially on the deserialization side). So far Queryable is not.
Though there are some potential ambiguities even for Queryable, e.g. is the struct an Object or a NamedTuple, is the Vec a Set, an Array or even Bytes.
One problem with abstracting over
ValueandQueryableis context dependent (de-)serialization.Valueis context dependent (especially on the deserialization side). So farQueryableis not.Though there are some potential ambiguities even for
Queryable, e.g. is the struct an Object or a NamedTuple, is the Vec a Set, an Array or even Bytes.
Not sure I understand the exact problem you're trying to explain. With Queryable you're going to change the way it's decoded by:
#[derive(Queryable)]
#[edgedb(NamedTuple)] // or Tuple
struct Struct {...}
Default is a shape (subset of the fields in an Object) because this is how we expect users to use edgeql. Set might be decoded into any kind of container (Vec, HashSet, BTreeSet, may be even maps, but I'm not sure).
Similar thing for encoding as QueryArgs. We only support certain kinds of arguments (no sets, only arrays, no tuples...)
Vec<u8> is probably more problematic. But we probably not going to implement QueryArg for u8 since there is no such type in edgedb. Minimum int is int16. So Vec<u8> is always bytes and doesn't conflict with Vec<T> where T:QueryArg as long as I understand.
With Queryable you're going to change the way it's decoded by
That's one way to resolve is ambiguity.
we probably not going to implement QueryArg for
u8
This comes down to how you view the relationship between code and the database.
- The database schema is most important. The code is merely a way to conveniently access it and should mirror the database's representation as closely as possible.
u8should not implementQueryablesince the database doesn't have a corresponding type. - The code represents the data in a way that makes sense for it. The database is a place to store and query it. The driver should choose the best way to represent any given type in the code.
u8should implementQueryableand be mapped toi16.
In my experience the second approach worked very well with document databases and avoided the need for having a separate domain model. But I'm not certain how well it'd work with edgedb.
Setmight be decoded into any kind of container
The problem with that is that both sets and arrays should be represented as containers in code, so there is still some ambiguity here. Fortunately arrays and sets have the same binary representation, so it's be possible to ignore this distinction.
There is one minor problem with that: Currently invalid headers produce different DecodeErrors for arrays and sets. You'd have to introduce a less specific error used when Queryable decodes an array/set.
One problem with abstracting over Value and Queryable is context dependent (de-)serialization. Value is context dependent (especially on the deserialization side). So far Queryable is not.
That problem still needs to be solved. Perhaps Queryable should be split into two traits, Decode and QueryResult where the decode function of QueryResult receives enough shape information for Value to work.
@tailhook
But we probably not going to implement QueryArg for u8 since there is no such type in edgedb.
We should add it, I don't think we omitted it deliberately.
The code represents the data in a way that makes sense for it. The database is a place to store and query it. The driver should choose the best way to represent any given type in the code. u8 should implement Queryable and be mapped to i16.
I'd prefer the driver to not chose anything on its own. Ideally types should be mapped 1 to 1.
While all of the things in the checklist here are worthwile and should be considered before an 1.0 release, I'm not convinced they should really be stoppers for a 0.1 (or 0.0.1) push to crates.io.
Even while explicitly not promising api stability, it might be a good idea to make it easy for people to test the things as they are, and, if nothing else, to reserve the name.
I'll consider reserving name. But why publishing release that breaks every now and then is better than using git url for the same purpose? I know you can't publish on crates.io with git dependency, but that doesn't stop anyone from using edgedb in their own projects. Or you want to publish some tool that uses edgedb protocol?
Mainly I just think a series of rough prereleases would be nicer to work with than using the code straight from git. I'm not in a hurry, but it seems to me that actually fixing everything in the checklist will require years of development, and I think it would be nice to have something at crates.io earlier than that.
But that might just be me overestimating, so never mind.
Reserving the name might still be a good thing, though.
Mainly I just think a series of rough prereleases would be nicer to work with than using the code straight from git.
Yes I agree. But they have associated cost and we need a plan for having a set of coherent releases that are better than arbitrary git commits. Currently we're busy getting Beta version of the database itself. Let's see if we can prioritize Rust bindings afterwards...
Even while explicitly not promising api stability, it might be a good idea to make it easy for people to test the things as they are, and, if nothing else, to reserve the name.
@kaj is right; I believe there's an answer that satisfies both the community and the core developers: cargo features.
Alternatively, we can look to Debian; they use an interesting philosophy that might work here. Their package manager's "unstable" repository dissuades the masses from consuming non-perfected libraries. Who knows, maybe an edgedb-rs-preview crate could be the perfect compromise.
Eventually, this package should get phased out in favor of the edgedb-rs crate provided that it has @tailhook stamp of approval.
Published 0.2.0 as the first step with no guarantees and a bunch of not implemented functionality.