feat(query): route carrier sum and count+sum reads through run_path_query - #805
Conversation
…uery Closes the last `NotSupported` in the unified read dispatch. Before this, `run_path_query`'s `AggregateCarrier` arm served only the count axis and refused the sum and count+sum kinds by name, pointing callers at prove_query + the per-key verifiers. #803 added the missing trusted readers, so the arm can now route all three axes: AggregateKind::Count -> query_aggregate_count_per_key AggregateKind::Sum -> query_aggregate_sum_per_key (new) AggregateKind::CountAndSum -> query_aggregate_count_and_sum_per_key (new) Every shape `classify()` can produce now has a reader behind it; the only `NotSupported` the dispatch still raises is the read-mode version gate. `PathQueryRun` gains two variants — `AggregateSumPerKey(Vec<(Vec<u8>, i64)>)` and `AggregateCountAndSumPerKey(Vec<(Vec<u8>, u64, i64)>)` — rather than collapsing the carrier family into one `Option`-bearing variant. Three reasons: it matches how the leaf family in the same enum is already shaped (`AggregateCount` / `AggregateSum` / `AggregateCountAndSum`); each axis maps 1:1 onto its reader's return type with no `Option` that is statically always-None; and `AggregateCountPerKey` is already public, so this stays additive. The original plan for this follow-up was to mirror a `VerifiedPathQuery::AggregatePerKey { per_key: Vec<(Vec<u8>, Option<u64>, Option<i64>)> }`, but that type does not exist — #798 shipped the read dispatch only, and its module doc notes the unified proof dispatch arrives separately. With no mirror target, matching the enum's own existing convention wins. When the unified verify dispatch does land it should mirror these three variants rather than the collapsed shape. Tests: `aggregate_carrier_count_matches_per_key_reader_and_others_are_refused` asserted the refusal, so it becomes `aggregate_carrier_all_kinds_match_their_per_key_readers` — a differential assertion per axis (unified answer == dedicated reader) plus a check of the values themselves, over ProvableSumTree and PCPS carrier fixtures. Adds `aggregate_carrier_per_key_dispatch_surfaces_reader_errors`: a non-tree outer match and a single-axis host under a combined carrier must fail through the dispatch with the same error text the dedicated reader produces, so routing can't paper over a reader's rejection. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Codecov Report✅ All modified and coverable lines are covered by tests. Additional details and impacted files@@ Coverage Diff @@
## develop #805 +/- ##
========================================
Coverage 92.27% 92.27%
========================================
Files 265 265
Lines 79920 79922 +2
========================================
+ Hits 73750 73752 +2
Misses 6170 6170
🚀 New features to boost your workflow:
|
|
Warning Review limit reached
Next review available in: 13 minutes Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available. How can I continue?After more reviews become available, a review can be triggered using the To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews. How do review limits work?CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability. For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window. Please refer docs for additional details. Review details⚙️ Run configurationConfiguration used: defaults Review profile: CHILL Plan: Pro Plus Run ID: 📒 Files selected for processing (2)
Comment |
Follow-up to #803 (merged) and #798 (merged) — the dispatch wiring both PRs left open.
What this closes
run_path_query'sAggregateCarrierarm served only the count axis and refused the other two by name:That was accurate when #798 landed — the readers didn't exist. #803 added them, so the arm now routes all three axes:
AggregateKindCountquery_aggregate_count_per_keySumquery_aggregate_sum_per_key(newly reachable)CountAndSumquery_aggregate_count_and_sum_per_key(newly reachable)Every shape
classify()can produce now has a reader behind it. The onlyNotSupportedthe dispatch still raises is the read-mode version gate, which is intentional and unrelated. Module doc updated to say so.The result-type decision
PathQueryRungains two variants rather than collapsing the carrier family:The plan for this follow-up was to mirror a
VerifiedPathQuery::AggregatePerKey { per_key: Vec<(Vec<u8>, Option<u64>, Option<i64>)> }. That type does not exist anywhere in the repo — #798 shipped the read dispatch only, and its own module doc notes that "the unified proof dispatch arrives separately." With no mirror target, three typed variants win on every axis I can find:AggregateCount(u64)/AggregateSum(i64)/AggregateCountAndSum { count, sum }. Collapsing only the carrier half would make the two halves disagree.Option<u64>would be statically always-Noneon that axis — the collapsed shape encodes a value that can't occur.AggregateCountPerKeyis already public on develop; collapsing it is a breaking change to a just-shipped variant.When the unified verify dispatch lands, it should mirror these three variants rather than the collapsed shape.
Tests
aggregate_carrier_count_matches_per_key_reader_and_others_are_refusedexisted to assert the refusal, so it is nowaggregate_carrier_all_kinds_match_their_per_key_readers: a differential assertion per axis (unified answer == dedicated reader) overProvableSumTreeand PCPS carrier fixtures, plus a check of the values themselves so the test would catch both readers being wrong in the same way.New:
aggregate_carrier_per_key_dispatch_surfaces_reader_errors. Routing must not paper over a reader's rejection, so a non-tree outer match and a single-axis host under a combined carrier each have to fail throughrun_path_querywith the same error text the dedicated reader produces.Validation
cargo clippy --workspace --all-features -- -D warnings— clean (exit 0, zero diagnostics)cargo test -p grovedb -p grovedb-query --all-features— 2633 + 255 pass, 0 failcargo build --no-default-features --features verify -p grovedb— cleancargo fmt --check— cleanNo version gating: purely additive routing over readers that are already gated on their own existing slots (
query_aggregate_{sum,count_and_sum}_on_range), so no new slots and no V4 gate.🤖 Generated with Claude Code