Repository navigation
Commit c1f4d84
authored
* chore(api): introduce RowAccessor + change FromRow trait signature (#61, #62 part 1)
Foundation commit for the FromRow modernization PR. Introduces
RowAccessor and the Row::get_by_name accessor; changes the FromRow
trait signature from `&Row` to `RowAccessor<'_>`; deletes the 1/2/3/4-
tuple FromRow impls; migrates the 3 hand-written FromRow impls; and
wires the cached column-name -> index lookup through fetch_one_as /
fetch_all_as (sync + async).
This is a breaking change to the FromRow trait. Lands as `chore:` to
defer release-please version bump until the v0.3.0 rollup commit.
What changed:
- New `hyperdb-api/src/row_accessor.rs` with `RowAccessor<'a>`. Borrows
&Row + a pre-built HashMap<&str, usize>. Methods: `get<T>(name)`,
`get_opt<T>(name)`, `position<T>(idx)`, `row()`. Errors map to
Error::Column { kind: Missing | Null | TypeMismatch } and
Error::ColumnIndexOutOfBounds. Includes 7 unit tests covering each
error path and the happy paths.
- New `Row::get_by_name<T>(name)` for hand-coded paths that don't go
through FromRow. Uses ResultSchema::column_index (linear scan). Doc
recommends #[derive(FromRow)] or fetch_*_as for hot paths (both
build the cache once per query).
- FromRow trait signature changed: `fn from_row(row: &Row)` ->
`fn from_row(row: RowAccessor<'_>)`. Trait rustdoc updated with the
recommended derive form (preview for the upcoming proc-macro crate)
and a hand-written impl example using `row.get(...)` /
`row.get_opt(...)`.
- Deleted the 4 tuple FromRow impls (1/2/3/4-tuple). Migration:
callers define a struct with #[derive(FromRow)] (added in next
commit) or use Row::get(idx) directly.
- Migrated the 3 hand-written FromRow impls to the new shape:
TestUser (tests/remaining_features_tests.rs), User
(tests/async_connection_tests.rs), Order
(examples/async_parity_smoke.rs).
- fetch_one_as / fetch_all_as (4 sites: sync + async) now build the
HashMap lookup once per query from the result schema, then construct
a RowAccessor per row. All rows in a result set share the same
Arc<ResultSchema>, so this is safe.
- Doctest examples in connection.rs and result.rs updated to the new
trait shape; the example_raw_transaction direct call to
Order::from_row in async_parity_smoke.rs replaced with a
fetch_all_as call (since the trait now takes a RowAccessor that
needs pre-built indices).
Verification:
- cargo build --workspace --all-targets -- clean
- cargo clippy --workspace --all-targets -- -D warnings -- clean
- cargo test --workspace -- all targets pass (including doctests)
- cargo fmt --check -- clean
* chore(api): add hyperdb-api-derive crate with #[derive(FromRow)] (#61, #62 part 2)
Adds the proc-macro crate `hyperdb-api-derive` that provides
`#[derive(FromRow)]`. Re-exported from `hyperdb-api` so callers don't
need a direct dependency — same pattern as serde / thiserror.
What changed:
- New workspace member `hyperdb-api-derive/` with `proc-macro = true`,
deps on syn v2 (with the `full` feature), quote, proc-macro2.
- `#[derive(FromRow)]` proc-macro generates an `impl FromRow` that
uses the `RowAccessor` API from Commit 1: `row.get("col")?` for
required fields, `row.get_opt("col")?` for `Option<T>` fields.
Field name → column name match is exact by default; the
`#[hyperdb(rename = "...")]` attribute overrides on a per-field
basis.
- Helpful compile errors for unsupported shapes:
- Tuple structs → "tuple-struct fields are not supported"
- Enums → "FromRow cannot be derived on enums"
- Unions → "FromRow cannot be derived on unions"
- Unknown attribute → "unrecognized hyperdb attribute `{x}`;
expected `rename = \"...\"`"
- `hyperdb-api`'s lib.rs adds `pub use hyperdb_api_derive::FromRow;`
alongside the trait re-export. The derive macro and the trait share
the name "FromRow" (Rust treats them as different namespaces).
- 3 integration tests in `hyperdb-api/tests/remaining_features_tests.rs`:
- `test_derive_from_row_parity_with_handwritten`: derived
`TestUserDerived` produces identical values to the hand-written
`TestUser` impl for the same query.
- `test_derive_from_row_with_rename`: `#[hyperdb(rename = "score")]`
redirects field-name lookup.
- `test_derive_from_row_missing_column_errors`: a derived struct
with a column not in the SELECT list surfaces as
`Error::Column { kind: Missing }`.
Verification:
- cargo build --workspace --all-targets — clean
- cargo clippy --workspace --all-targets -- -D warnings — clean
- cargo test --workspace --lib + --doc — all pass
- cargo fmt --check — clean
* docs(api): add #61+#62 migration recipes to MIGRATING-0.3.md
Adds a "FromRow modernization" section to the consolidated migration
guide covering:
- Trait signature change (&Row -> RowAccessor<'_>)
- Tuple impl deletion + recipes for migrating (define a struct with
#[derive(FromRow)] or use Row::get(idx) directly)
- New #[derive(FromRow)] proc-macro with #[hyperdb(rename = "...")]
- New Row::get_by_name accessor
- Error::Column / ColumnErrorKind error shape (already in #70)
- Performance note about cached-index lookup vs. linear scan
- Note that hyperdb-api-derive doesn't need to be a direct dep
Also fixes a broken intra-doc link in row_accessor.rs (FromRow ->
crate::FromRow); doc-warning count back to baseline 6.
Verification:
- cargo build --workspace --all-targets — clean
- cargo doc --workspace --no-deps — 6 warnings (= post-#70 baseline)
- cargo fmt --check — clean
* chore(api): address final-review findings on FromRow modernization (#61, #62 part 4)
Three findings from the architectural pre-PR review:
- M1 (consistency): RowAccessor::position now produces Error::Column
with ColumnErrorKind::Null / TypeMismatch (synthesized name
"col[{idx}]") instead of Error::Conversion. This aligns positional
errors with the named-access error shape, so callers can match on
Error::Column { kind, .. } uniformly across get/get_opt/position
rather than special-casing positional access. Out-of-bounds still
uses Error::ColumnIndexOutOfBounds for index integrity. Added a
dedicated unit test (position_null_errors_with_kind_null).
- m1 (API surface): Removed RowAccessor::row(). It was a leak vector
that let FromRow impls drop down to bare Row methods, bypassing the
cached-index lookup the accessor exists to provide — exactly the
anti-pattern this PR set out to eliminate. With no production caller
needing it (verified by build), removing rather than gating to
pub(crate) is the cleanest move. Easy to add back if a real consumer
surfaces.
- m2 (forward-compat): Derive macro's "unrecognized hyperdb attribute"
error now reads "supported attributes: rename" instead of pinning a
specific syntax. When new attributes (skip, default, with) ship in
v0.3.x, the message stays accurate without a macro patch.
Verification:
- cargo build --workspace --all-targets — clean
- cargo clippy --workspace --all-targets -- -D warnings — clean
- cargo test --workspace --lib — 8 row_accessor tests pass (incl. new
position_null_errors_with_kind_null)
- cargo fmt --check — clean
* chore(api): add #[hyperdb(index = N)] derive attribute + RowAccessor::position_opt
Extends #[derive(FromRow)] with a positional access mode for queries
where columns have no stable name (e.g. SELECT id, COUNT(*) FROM ...
GROUP BY id). Mutually exclusive with #[hyperdb(rename = "...")].
Adds RowAccessor::position_opt as the symmetric Option<T> counterpart
to position, mirroring the get/get_opt naming pair. NULL becomes None;
out-of-bounds and type-mismatch still error.
The macro emits position(N)? for non-Option fields and position_opt(N)?
for Option<T> fields, matching the existing get/get_opt dispatch on
name-based access.
Updates MIGRATING-0.3.md and the derive crate README. Doc-warning
count back at baseline 6.
Verification:
- cargo build/clippy/test/fmt/doc all clean on workspace
- new RowAccessor::position_opt unit tests (NULL, happy path, OOB)
- new integration test test_derive_from_row_with_index runs against
a real query with COUNT(*) (unnamed column)
* docs(derive): clarify RowAccessor accessor pairs and zero-based indexing in README
1 parent 5c7e78b commit c1f4d84
16 files changed
Lines changed: 1231 additions & 109 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
3 | 3 | | |
4 | 4 | | |
5 | 5 | | |
| 6 | + | |
6 | 7 | | |
7 | 8 | | |
8 | 9 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
243 | 243 | | |
244 | 244 | | |
245 | 245 | | |
| 246 | + | |
| 247 | + | |
| 248 | + | |
| 249 | + | |
| 250 | + | |
| 251 | + | |
| 252 | + | |
| 253 | + | |
| 254 | + | |
| 255 | + | |
| 256 | + | |
| 257 | + | |
| 258 | + | |
| 259 | + | |
| 260 | + | |
| 261 | + | |
| 262 | + | |
| 263 | + | |
| 264 | + | |
| 265 | + | |
| 266 | + | |
| 267 | + | |
| 268 | + | |
| 269 | + | |
| 270 | + | |
| 271 | + | |
| 272 | + | |
| 273 | + | |
| 274 | + | |
| 275 | + | |
| 276 | + | |
| 277 | + | |
| 278 | + | |
| 279 | + | |
| 280 | + | |
| 281 | + | |
| 282 | + | |
| 283 | + | |
| 284 | + | |
| 285 | + | |
| 286 | + | |
| 287 | + | |
| 288 | + | |
| 289 | + | |
| 290 | + | |
| 291 | + | |
| 292 | + | |
| 293 | + | |
| 294 | + | |
| 295 | + | |
| 296 | + | |
| 297 | + | |
| 298 | + | |
| 299 | + | |
| 300 | + | |
| 301 | + | |
| 302 | + | |
| 303 | + | |
| 304 | + | |
| 305 | + | |
| 306 | + | |
| 307 | + | |
| 308 | + | |
| 309 | + | |
| 310 | + | |
| 311 | + | |
| 312 | + | |
| 313 | + | |
| 314 | + | |
| 315 | + | |
| 316 | + | |
| 317 | + | |
| 318 | + | |
| 319 | + | |
| 320 | + | |
| 321 | + | |
| 322 | + | |
| 323 | + | |
| 324 | + | |
| 325 | + | |
| 326 | + | |
| 327 | + | |
| 328 | + | |
| 329 | + | |
| 330 | + | |
| 331 | + | |
| 332 | + | |
| 333 | + | |
| 334 | + | |
| 335 | + | |
| 336 | + | |
| 337 | + | |
| 338 | + | |
| 339 | + | |
| 340 | + | |
| 341 | + | |
| 342 | + | |
| 343 | + | |
| 344 | + | |
| 345 | + | |
| 346 | + | |
| 347 | + | |
| 348 | + | |
| 349 | + | |
| 350 | + | |
| 351 | + | |
| 352 | + | |
| 353 | + | |
| 354 | + | |
| 355 | + | |
| 356 | + | |
| 357 | + | |
| 358 | + | |
| 359 | + | |
| 360 | + | |
| 361 | + | |
| 362 | + | |
| 363 | + | |
| 364 | + | |
| 365 | + | |
| 366 | + | |
| 367 | + | |
| 368 | + | |
| 369 | + | |
| 370 | + | |
| 371 | + | |
| 372 | + | |
| 373 | + | |
| 374 | + | |
| 375 | + | |
246 | 376 | | |
247 | 377 | | |
248 | 378 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
| 1 | + | |
| 2 | + | |
| 3 | + | |
| 4 | + | |
| 5 | + | |
| 6 | + | |
| 7 | + | |
| 8 | + | |
| 9 | + | |
| 10 | + | |
| 11 | + | |
| 12 | + | |
| 13 | + | |
| 14 | + | |
| 15 | + | |
| 16 | + | |
| 17 | + | |
| 18 | + | |
| 19 | + | |
| 20 | + | |
| 21 | + | |
| 22 | + | |
| 23 | + | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
| 1 | + | |
| 2 | + | |
| 3 | + | |
| 4 | + | |
| 5 | + | |
| 6 | + | |
| 7 | + | |
| 8 | + | |
| 9 | + | |
| 10 | + | |
| 11 | + | |
| 12 | + | |
| 13 | + | |
| 14 | + | |
| 15 | + | |
| 16 | + | |
| 17 | + | |
| 18 | + | |
| 19 | + | |
| 20 | + | |
| 21 | + | |
| 22 | + | |
| 23 | + | |
| 24 | + | |
| 25 | + | |
| 26 | + | |
| 27 | + | |
| 28 | + | |
| 29 | + | |
| 30 | + | |
| 31 | + | |
| 32 | + | |
| 33 | + | |
| 34 | + | |
| 35 | + | |
| 36 | + | |
| 37 | + | |
| 38 | + | |
| 39 | + | |
| 40 | + | |
| 41 | + | |
| 42 | + | |
| 43 | + | |
| 44 | + | |
| 45 | + | |
| 46 | + | |
| 47 | + | |
| 48 | + | |
| 49 | + | |
| 50 | + | |
| 51 | + | |
| 52 | + | |
| 53 | + | |
| 54 | + | |
| 55 | + | |
| 56 | + | |
| 57 | + | |
| 58 | + | |
| 59 | + | |
| 60 | + | |
| 61 | + | |
| 62 | + | |
| 63 | + | |
| 64 | + | |
| 65 | + | |
| 66 | + | |
| 67 | + | |
| 68 | + | |
| 69 | + | |
| 70 | + | |
| 71 | + | |
| 72 | + | |
| 73 | + | |
| 74 | + | |
| 75 | + | |
| 76 | + | |
| 77 | + | |
| 78 | + | |
| 79 | + | |
| 80 | + | |
| 81 | + | |
| 82 | + | |
| 83 | + | |
| 84 | + | |
| 85 | + | |
| 86 | + | |
| 87 | + | |
| 88 | + | |
| 89 | + | |
| 90 | + | |
| 91 | + | |
| 92 | + | |
| 93 | + | |
| 94 | + | |
| 95 | + | |
| 96 | + | |
| 97 | + | |
| 98 | + | |
| 99 | + | |
| 100 | + | |
| 101 | + | |
| 102 | + | |
| 103 | + | |
| 104 | + | |
| 105 | + | |
| 106 | + | |
| 107 | + | |
| 108 | + | |
| 109 | + | |
| 110 | + | |
| 111 | + | |
| 112 | + | |
| 113 | + | |
| 114 | + | |
| 115 | + | |
| 116 | + | |
| 117 | + | |
| 118 | + | |
| 119 | + | |
| 120 | + | |
| 121 | + | |
| 122 | + | |
| 123 | + | |
| 124 | + | |
| 125 | + | |
| 126 | + | |
| 127 | + | |
| 128 | + | |
| 129 | + | |
| 130 | + | |
| 131 | + | |
| 132 | + | |
0 commit comments