Conversation
PEP 249 is what a Python program expects a database to look like, and the code that expects it is not always code anyone can change: a dashboard, a test harness, a helper somebody wrote against sqlite3 five years ago. zudb.dbapi is that shape over the same connection, and it is a layer rather than a second client. A statement goes to the same execute underneath, the rows are the same rows, and nothing here reimplements anything. Parameters are ?, which PEP 249 calls qmark, rewritten into the engine's own $name before the statement runs. The named style cannot work here: :name is how a pattern names its label, so (p:person) and WHERE p.uid = :uid cannot be told apart without parsing the statement. A question mark is a character GQL has no meaning for anywhere, which makes it unambiguous, and the scanner that finds them follows the lexer's own rules so that a ? inside a string, a raw string, a quoted name or a comment is text somebody wrote and stays that way. The name a marker is rewritten to is checked against the statement first, since a value bound over the top of somebody's own text is a wrong answer with no error in it. A dict hands the statement over untouched, so $name still works for anyone writing zu statements rather than generating them. Transactions are implicit, which PEP 249 requires and the native client does not do: one opens before the first statement after each commit or rollback, and closing a connection rolls back what was not committed. That is the one place the two clients disagree about what a program meant, so connect(autocommit=True) turns it off and gives the native behaviour back. The exception classes are both hierarchies at once rather than two sitting side by side. zudb.Error is the Error PEP 249 asks for, and each class the engine raises arrives as a class that is both: a syntax error is a zudb.SyntaxError and a dbapi.ProgrammingError and the same object, carrying the code, the condition, the position, the excerpt and the documentation link the engine wrote. A driver-shaped library and code written against this client can catch the same failure in the same program. description gives the Python type of what each column holds, read from the first rows, because a result declares no types of its own and a guess would be worse than a None. The rows read to work that out are handed back to the caller first, so typing a result takes none of it. rowcount is rows produced and is -1 after a write, since the engine does not count what a write touched and a number nobody can stand behind is worse than the absence of one. callproc and nextset are left out entirely rather than defined and refused, which is what PEP 249 asks for. 51 tests, and zudb.dbapi is a submodule to ask for by name so that importing zudb still costs what it costs.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
zudb.dbapiis PEP 249 over the same connection. It is a layer and not a second client: a statement goes to the sameexecuteunderneath, the rows are the same rows, and nothing here reimplements anything. It is worth having even where the native client is nicer, because the code that expects a driver is not always code anyone can change.Parameters are
?, which PEP 249 callsqmark, rewritten into the engine's own$namebefore the statement runs. Thenamedstyle cannot work here::nameis how a pattern names its label, so(p:person)andWHERE p.uid = :uidcannot be told apart without parsing the statement, while?is a character GQL has no meaning for anywhere. The scanner that finds them follows the lexer's own rules, so a?inside a string, a raw@'...'string, a backtick-quoted name or either kind of comment is text somebody wrote and stays that way. The name a marker is rewritten to is checked against the statement first, because a value bound over the top of somebody's own text is a wrong answer with no error in it. A dict hands the statement over untouched, so$namestill works for anyone writing zu statements rather than generating them.Transactions are implicit, which PEP 249 requires and the native client does not do: one opens before the first statement after each
commitorrollback, and closing a connection rolls back what was not committed. That is the one place the two clients disagree about what a program meant, soconnect(autocommit=True)turns it off and gives the native behaviour back. A read-only connection opens a read-only transaction, or its first statement would be refused before it ran.The exception classes are both hierarchies at once rather than two sitting side by side.
zudb.Erroris theErrorPEP 249 asks for, and each class the engine raises arrives as a class that is both: a syntax error is azudb.SyntaxErrorand adbapi.ProgrammingErrorand the same object, carrying the code, the condition, the position, the excerpt and the documentation link the engine wrote. So a driver-shaped library and code written against this client can catch the same failure in the same program. The original traceback is kept and the chain is suppressed, since an exception that is an instance of the class it came from does not need printing twice.descriptiongives the Python type of what each column holds, read from the first rows, because a result declares no types of its own and a guess would be worse than aNone. The rows read to work that out are handed to the caller first, so typing a result takes none of it.rowcountis rows produced and is -1 after a write, since the engine does not count what a write touched.callprocandnextsetare left out entirely rather than defined and refused, which is what PEP 249 asks for.conn.zuandcur.resultare the way back out to the rest of the client.51 new tests.
zudb.dbapiis a submodule to ask for by name, with a test that says importingzudbdoes not pull it in. Local gate is green: ruff, cargo fmt, clippy and the full suite at 580 passed.