fix(api): parameterized FromRow methods (fetch_*_as_params / stream_as_params)#152
Merged
StefanSteiner merged 4 commits intoJun 16, 2026
Conversation
Add fetch_one_as_params, fetch_all_as_params, and stream_as_params on Connection — the intersection of FromRow struct-mapping and ToSqlParam parameter binding. Each delegates to query_params (binary Parse/Bind/ Execute) then maps rows exactly as the existing *_as methods do; the prepared-statement guard rides along on the returned Rowset, so Drop ordering is preserved. Round-trip tests cover single/all/stream, multi-param binding, and the empty-result error path.
Mirror the sync fetch_one_as_params / fetch_all_as_params / stream_as_params on AsyncConnection. fetch_one/fetch_all delegate to query_params; stream_as_params encodes params before entering the try_stream! generator (it can't borrow &[&dyn ToSqlParam] across await points) and rebuilds query_params' prepare/execute sequence inline, carrying the statement guard. The Prepared path exposes its schema at prepare time, so the column-index map is built once up front. gRPC FeatureNotSupported surfaces as the stream's first yielded item (documented), not eagerly. Async round-trip tests cover all three.
- FromRow and ToSqlParam rustdoc cross-reference the new _as_params trio. - row_mapping_forms example gains Form 6 (fetch_all_as_params + stream_as_params), wired into main and verified end-to-end. - docs/ROW_MAPPING.md: new 'Form 6 — Parameterized struct mapping' section + Choosing-a-form row; retitled 'Six Forms'. - README: note + snippet under Parameterized Queries. - Fix stale async query_params rustdoc that described text-mode escaping; it has done binary Parse/Bind/Execute since the sync parity work.
Final-sweep reviewer caught line 9 still saying 'All five forms' after the doc was retitled to Six Forms.
This was referenced Jun 16, 2026
Merged
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.
Summary
Adds the missing intersection of
FromRowstruct-mapping andToSqlParamparameter binding (issue #137). Before this, you could map rows into a struct
(
fetch_*_as/stream_as, plain&strqueries) or bind$Nparameters(
query_params, rawRows) — but not both in one call. A parameterizedSELECT … WHERE org_id = $1you wanted as aVec<User>forced a manualRowAccessor/row.get()loop.New
_as_paramstrio on bothConnectionandAsyncConnection:fetch_one_as_params::<T>(query, params) -> Result<T>fetch_all_as_params::<T>(query, params) -> Result<Vec<T>>stream_as_params::<T>(query, params) -> Result<impl Iterator<Item = Result<T>>>(async:
impl Stream<Item = Result<T>>, no outerResult)Design
The two halves already existed — this plumbs them together.
fetch_one_as_params/fetch_all_as_paramsdelegate toquery_params, which already returns aRowset/AsyncRowsetcarrying theprepared-statement guard.
TypedRowIterator::newtakes the whole rowset, soDrop ordering (close_statement after the rowset releases its connection
lock) is preserved with no extra code. Rows are mapped exactly as the existing
*_asmethods (build_indicesonce +FromRow::from_rowper row).stream_as_paramsis the exception:&[&dyn ToSqlParam]can't cross thetry_stream!await points, so it encodes params up front (encoding needs noconnection) and rebuilds
query_params' Parse/Bind/Execute sequence inline,carrying the statement guard. The Prepared path exposes its schema at prepare
time, so the column-index map is built once up front. A "keep in sync with
query_params" comment marks the duplication.Naming:
_as_paramscomposes the two existing axes (_as= map to struct,_params= bind params) in their established reading order. Both plan reviewersendorsed it over
_params_as.Error semantics
fetch_one_as_paramsreturns"Query returned no rows"on empty results(same as
fetch_one_as).stream_as_paramssurfaces stream-open errors — includingFeatureNotSupportedon gRPC (prepared statements are TCP-only) — on theouter
Result; per-row mapping errors come back as each item'sResult.stream_as_paramshas no outerResult, so submission failures(incl. the gRPC
FeatureNotSupported) surface as the first yieldedErritem.Docs & examples
FromRowandToSqlParamrustdoc cross-reference the new methods.row_mapping_formsexample gains Form 6 (verified end-to-end).docs/ROW_MAPPING.md: new "Form 6 — Parameterized struct mapping" section +Choosing-a-form row; retitled "Six Forms".
query_paramsrustdoc that described "text-modeescaping" — it has done binary Parse/Bind/Execute since the async-parity work.
Out of scope (deliberate)
opt-in per binding, so adding Rust methods doesn't auto-expose them.
query_params; not widened here.Test plan
cargo clippy --workspace --all-targets -- -D warnings— cleancargo test -p hyperdb-api— full suite green (8 new tests: 5 sync + 3 async)cargo test -p hyperdb-api --doc— 171 passedcargo run -p hyperdb-api --example row_mapping_forms— Form 6 filtersprice < $15→ 3 rowscargo build --workspace— greenAcceptance criteria (#137)
$1-boundToSqlParam→#[derive(FromRow)]struct,asserting values (plus multi-param and empty-result edge cases).
FromRowandToSqlParammodule docscross-reference the new methods.
Closes #137.