Conversation
Three calls on a connection, and a class for the first of them. `prepare` compiles a statement now and hands back something that runs it later, as often as it is asked to, with different values bound each time. `explain` compiles one and answers the plan without running it. `profile` runs it and answers what the operators really did. A prepared statement answers the same three ways a connection does, because `QueryTask` grew a `Source` that is either a text or a pinned id and the three answer shapes were already written against it. So `query`, `exec` and `columnar` are on `Prepared` for the cost of handing them an id. There is no `stream`, deliberately: the engine's streaming path takes a text rather than an id, so a streamed prepared statement would be this client running the text again behind the caller. What preparing buys here is not throughput, and the bench prints that rather than hiding it. The engine caches a plan by its text, so a loop that prepares and a loop that repeats the same string are the same speed, 13756 ns against 13812 ns. A text that is new every run costs 21209 ns, and that gap is the whole of what binding parameters saves a program that would otherwise paste its values in. What preparing does buy is the compile happening at the line that asked, at startup, and the parameter names coming back. The plan comes back twice: `root` is the tree, for a program that wants to assert an operator without matching a string, and `text` is the engine's own listing, for a person. It is the engine's rendering rather than this client's so the two cannot drift. Profile counts are `number` and not `bigint`, which is the one place here an integer is spelled as a double on purpose, since nothing a profile counts comes near 2^53. 39 tests over the lifetime, the shapes and the refusals, a bench, the README sections and a type fixture in each format.
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.
conn.prepare,conn.explainandconn.profile, and aPreparedclass for the first of them.Preparing
preparecompiles a statement and hands back something that runs it later, as often as it is asked to, with different values bound each run. It answers the same three ways a connection does,query,execandcolumnar, and the options go on the run rather than on the prepare, since which run a caller wants to stop or spell as numbers is a property of that run.That came almost free:
QueryTaskgrew aSourcethat is either a text or a pinned id, so the three answer shapes were already written and a prepared statement reaches all of them by handing them an id.Handlescarries the three counted handles every call runs against, which is also what keeps the constructor under clippy's argument limit.There is no
streamon a prepared statement, deliberately. The engine's streaming path takes a source text rather than a pinned id, so a streamed prepared statement would be this client quietly running the text again behind the caller, and a method that does not do what its name says is worse than one that is not there.What preparing buys is not throughput, and the bench prints that rather than hiding it. A driver prepares to save a round trip and there is none here, and the engine already caches a plan by its text.
npm run bench:preparedover 100 rows and 5000 runs:The first two being equal is the honest result. The third is the line to read: a statement whose text differs every run, which is what a program that pastes its values into the string is writing, pays the compile every time. What preparing does buy is the compile happening at the line that asked, at startup, where a statement that does not compile fails on the way up, and the parameter names coming back in
params.Explaining and profiling
The plan comes back twice on purpose.
rootis the tree, so a test can assert that a scan became a seek without matching on a string, and every operator carries itsdetail,binds,tables,children, the bracket it is inside and the name the listing gives it.textis the engine's own rendering of that same tree, so the two cannot drift apart from one release to the next.explaintakes no parameters, since a plan is chosen from the shape of the statement and a plan asked for with values would suggest the values changed it.profiletakes bindings, since it is a run. Every count is anumberand not abigint, which is the one place in this client an integer is spelled as a double on purpose: nothing a profile counts comes near 2^53 and a caller doing arithmetic on a measurement should not have to convert first. A statement that writes is refused rather than profiled, because a profile that also inserted two rows changed the thing it measured.Checked
39 new tests, over the lifetime of a prepared statement rather than only over the rows it answers: what it says after it is closed, what happens to one whose connection went first, closing twice,
await using, a read-only connection, a signal on one run, a mode on one run, and three prepared at once. The plan tests assert the listing against the tree it was rendered from rather than against a string written here, so an optimizer that learns to print one better does not fail them.Full suite is 311 tests, all green, along with
cargo fmt,cargo clippy --all-features,npm run check:types,npm run check:packageand the api report.Milestone: DX3 (tamnd/zu#169).