Summary
fetch_all_as::<T>() is the only public API that wires FromRow into typed struct mapping, but it calls fetch_all first — collecting all rows into a Vec<Row> before any mapping occurs. Memory usage is O(total rows), which makes it unusable for large or unbounded result sets.
The streaming path (execute_query + next_chunk / rows()) gives back untyped Row values only. RowAccessor::new and RowAccessor::build_indices are both pub(crate), so callers cannot wire a #[derive(FromRow)] struct into the chunk loop themselves.
Proposed API
Add a streaming typed iterator to Connection (and the async equivalent on AsyncConnection):
/// Streams rows mapped to `T` via `FromRow`, one at a time.
/// Builds the column-name → index map once from the first chunk's schema;
/// memory usage is O(chunk_size), not O(total_rows).
fn stream_as<T: FromRow>(&self, query: &str) -> impl Iterator<Item = Result<T>> + '_;
The implementation would:
- Call
execute_query to get a streaming Rowset
- Build
RowAccessor::build_indices once from the first chunk's schema
- Yield
T::from_row(RowAccessor::new(&row, &indices)) for each row across all chunks
Workaround today
For streaming millions of rows with a named struct, callers must use rows() + row.get_by_name("col") (linear scan per field per row) or positional row.get(N) (fragile column ordering). Neither composes with #[derive(FromRow)].
Acceptance criteria
Summary
fetch_all_as::<T>()is the only public API that wiresFromRowinto typed struct mapping, but it callsfetch_allfirst — collecting all rows into aVec<Row>before any mapping occurs. Memory usage is O(total rows), which makes it unusable for large or unbounded result sets.The streaming path (
execute_query+next_chunk/rows()) gives back untypedRowvalues only.RowAccessor::newandRowAccessor::build_indicesare bothpub(crate), so callers cannot wire a#[derive(FromRow)]struct into the chunk loop themselves.Proposed API
Add a streaming typed iterator to
Connection(and the async equivalent onAsyncConnection):The implementation would:
execute_queryto get a streamingRowsetRowAccessor::build_indicesonce from the first chunk's schemaT::from_row(RowAccessor::new(&row, &indices))for each row across all chunksWorkaround today
For streaming millions of rows with a named struct, callers must use
rows()+row.get_by_name("col")(linear scan per field per row) or positionalrow.get(N)(fragile column ordering). Neither composes with#[derive(FromRow)].Acceptance criteria
Connection::stream_as::<T>(query)returns a lazy iterator ofResult<T>AsyncConnectiongets an equivalentstream_asreturningimpl Stream<Item = Result<T>>docs/ROW_MAPPING.mdas Form 5