-
Notifications
You must be signed in to change notification settings - Fork 46
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
Why execute()
doesn't support arguments?
#51
Comments
Your confusion possibly stems from exposure to Python's DB API, where @1st1 let's consider alternative names for the "statement" and the "script" methods, because I've seen similar confusion from asyncpg users. |
Yes
asyncpg is designed exactly the same way. We've never had a user requesting Most Python bindings adhere to the Python DB API which is broken on pretty much all levels. Like messed up arguments encoding, or inability to prepare statements, or misusing the "cursor" terminology. Unfortunately (and I really mean that) we can't use existing Python bindings as an example, simply because their API is broken.
I think it would just lead to more a more confusing UX. If we do it the way you suggest we will have bug reports like "why aren't arguments passed to all queries in the script I pass to the execute() method". As an alternative we can consider renaming @elprans what do you think? |
Some links from my slack conversation with @elprans:
There's some confusion with The bigger issue, though, is that people don't realize that they can use This isn't new though, we had multiple discussions about I'll start. One way would be to re-purpose
|
I started to write a big comment in Simple Query ProtocolOriginally we did have a 1 to 1 mapping between our
Extended Query ProtocolIn PostgreSQL the Simple Query protocol is just one way of executing queries. It can execute a batch of SQL queries but:
There's an alternative, called Extended Query protocol. It allows to return data in BINARY, to receive query parameters, to prepare named and anonymous statements, etc. EdgeDB Extended Query protocol is a pretty much one-to-one mapping to Postgres Extended Query protocol. We do a bunch of tricks on the server side to maintain an LRU cache and auto-prepare queries, but generally it's the same thing. EdgeDB Protocol & Python APICurrently in EdgeDB Python API we have the following APIs:
Paul's original question is valid: the inability of There's also another angle how to look at this: in asyncpg we have One way to add "executemany" would be to add a new protocol message that would receive an EdgeQL command(s) and a stream of arguments. This way in the future we can extend That new protocol flow will use a combination of Simple Query and Extended Query Postgres messages. The former is optimal for blocks of SQL without parameters, the latter can receive arguments. It's all complicated by the fact that we need to add cancellation handling to all of that, making this quite a tricky thing to implement correctly. We'll do that eventually, though. Therefore I think that right now it's better to do nothing. :) We can always extend our protocol with this "batch execute" message flow. We can at later point add arguments to Another thing to consider: adding all kinds of APIs to the Python driver means that we'd want to add them to all other supported languages — this significantly increases the surface for us. We don't have that time. We should focus on the hosted version and releasing a stable 1.0. In conclusion I'd say that the current API we have in Python is forward-compatible. Let's keep it as is. |
I'm not sure how @1st1's comment addresses the multi-statement issue. I mean. I'm okay if we rename But if we keep current semantics I don't think we can add arguments later and it will also require renaming |
@tailhook can you elaborate on this concern please? |
No, we rename nothing now. Later we add a new flow for execute and will be able to add args to
I'm also curious. |
The last two are hard to implement. So I propose to rename multi-statement function to |
How did you come to this conclusion? Seems straightforward to me, if we add an appropriate protocol message. |
Well, off the top of my head:
I'm not sure how far it's on the spectrum of just a bit more work or super-hard to implement. But I would start with single-statement impl first. |
By the way, does it change the design if we need execmany returning result? Say insert 10 objects and return ids? ( |
As a user, I would expect the last one as well. This is solved by documentation.
Sure, that's doesn't seem like a big problem.
The parameter type must be unambiguous, this is easy to validate and report. If you absolutely must pass the same value to multiple queries in a different type context, use text and casts or converters.
This is not a problem, as the entire script is parsed and validated as a unit.
Like with any functionality.
What issues? How is the situation different from an unparametrized script? |
Returning methods must be single-statement, because there is no reasonable way to reconcile the result type shape for arbitrary statements. Also, the only reason why you might need a separate If you want to insert and return, then you're likely inserting a small amount of data, and pipelining is only worth it on huge volumes, like ETL workloads, and there you are almost always better off by inserting with |
You're probably better at estimating this specific thing. If you think this is not hard enough I'm okay with that. Let's keep the issue open in the meantime until arguments are implemented. |
Latest thoughts on this:
The motivation to going away from the "fetch" term is explained in comments above; essentially it boils down to people being confused that "fetch" is probably coming from asyncpg / Python DB API; "query" is used in Golang / JS / Rust / etc. |
Looking back in retrospect from the college project I worked on w/ EdgeDB, I can attest to the Connection API being a bit confusing at first. Not just coming from Python, but also in Java DB APIs where it's rather common to use
I can agree that it's weird and not great from an API design perspective, but the reality is that many DB API users are very much used to it. Weirdness and poor design can easily become intuitive if exposed to it enough; I think that's human nature to some degree. That's not to say that EdgeDB's API should suffer in quality because of existing precedent of course, but IMO it should be taken into account in the docs. Thus, I think it would be worthwhile to briefly mention in the docs for If it would be helpful, I can work on the above in addition to the rename. I should have time in the next week, if not sooner, to open a PR (or two) for it. Quick question though: should there be some form of deprecation process or anything to smooth the transition from |
This commit renames the following APIs: * fetchall() -> query() * fetchone() -> query_one() * fetchall_json() -> query_json() * fetchone_json() -> query_one_json() The above methods were renamed for the async connection, the blocking connection, and for the async pool. See issue #51 for details.
This commit renames the following APIs: * fetchall() -> query() * fetchone() -> query_one() * fetchall_json() -> query_json() * fetchone_json() -> query_one_json() The above methods were renamed for the async connection, the blocking connection, and for the async pool. See issue #51 for details.
See geldata/gel-python#51 for the relevant discussion.
See geldata/gel-python#51 for the relevant discussion.
Absolutely, would appreciate PRs.
Of course, the change is backwards compatible. The old |
I'll keep the issue open until execute() gets support for
|
This has been implemented. |
See geldata/gel-python#51 for the relevant discussion.
My understanding that switching from
fetchone
toexecute
should be seamless if you don't need the result. But it's not the case:The error is confusing.
Technically, I understand that it's because there is such thing in a protocol. And that thing in protocol
may make sense for scripts or something, but it doesn't make sense from the point of view of the user of python bindings (other database bindings have no such issue as far as I remember).
So perhaps execute should accept arguments and silently switch to
fetchone
internally when at least one argument is specified?The text was updated successfully, but these errors were encountered: