Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
280 changes: 248 additions & 32 deletions grovedb-query/src/merge.rs

Large diffs are not rendered by default.

293 changes: 254 additions & 39 deletions grovedb-query/tests/merge_coverage.rs

Large diffs are not rendered by default.

7 changes: 4 additions & 3 deletions grovedb-query/tests/query_terminal_and_merge.rs
Original file line number Diff line number Diff line change
Expand Up @@ -118,7 +118,7 @@ fn merge_apis_cover_default_and_conditional_paths() {
other.insert_key(k(2));
other.set_subquery_path(vec![k(9)]);

base.merge_with(other);
base.merge_with(other).expect("merge must succeed");
assert_eq!(base.items.len(), 2);
assert!(base.items.contains(&QueryItem::Key(k(1))));
assert!(base.items.contains(&QueryItem::Key(k(2))));
Expand All @@ -129,7 +129,7 @@ fn merge_apis_cover_default_and_conditional_paths() {
.expect("conditional branch should be created");
assert!(conditional.contains_key(&QueryItem::Key(k(2))));

let merged_empty = Query::merge_multiple(vec![]);
let merged_empty = Query::merge_multiple(vec![]).expect("merge must succeed");
assert!(merged_empty.items.is_empty());

let mut left = Query::new_single_key(k(10));
Expand Down Expand Up @@ -172,7 +172,8 @@ fn merge_conditional_subquery_branches_splits_intersections() {
subquery_path: Some(vec![p(b"a")]),
subquery: None,
},
);
)
.expect("no read modes involved");

assert_eq!(merged.len(), 3);
assert!(merged.contains_key(&QueryItem::Range(k(1)..k(3))));
Expand Down
20 changes: 20 additions & 0 deletions grovedb-version/src/version/grovedb_versions.rs
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,25 @@ pub struct GroveDBPathQueryMethodVersions {
pub unified_read_mode: FeatureVersion,
}

/// Method versions for the standalone indexed-axis query family — the
/// per-axis reads and the echo-based proof envelopes
/// (`prove/verify_indexed_axis_*` and their per-axis wrappers). These
/// entry points predate this struct and shipped unversioned; the slots
/// exist so the first future divergence bumps a number instead of
/// forking behavior silently. The embedded (V1-envelope) axis shapes
/// are gated separately via
/// `GroveDBOperationsProofVersions::axis_descent_in_v1_envelope`.
#[derive(Clone, Debug, Default)]
pub struct GroveDBOperationsIndexedAxisVersions {
/// The trusted per-axis reads (`indexed_{count,sum,avg}_*`).
pub read: FeatureVersion,
/// The standalone single-path envelope provers
/// (`prove_indexed_axis_{top_k,top_k_paginated,query,rank_of_key,range_aggregate}`).
pub prove_single_path: FeatureVersion,
/// The matching standalone verifiers.
pub verify_single_path: FeatureVersion,
}

#[derive(Clone, Debug, Default)]
pub struct GroveDBApplyBatchVersions {
pub apply_batch_structure: FeatureVersion,
Expand Down Expand Up @@ -119,6 +138,7 @@ pub struct GroveDBOperationsVersions {
pub delete_up_tree: GroveDBOperationsDeleteUpTreeVersions,
pub query: GroveDBOperationsQueryVersions,
pub proof: GroveDBOperationsProofVersions,
pub indexed_axis: GroveDBOperationsIndexedAxisVersions,
pub average_case: GroveDBOperationsAverageCaseVersions,
pub worst_case: GroveDBOperationsWorstCaseVersions,
}
Expand Down
9 changes: 7 additions & 2 deletions grovedb-version/src/version/v1.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,8 @@ use crate::version::{
GroveDBApplyBatchVersions, GroveDBElementMethodVersions,
GroveDBOperationsAverageCaseVersions, GroveDBOperationsDeleteUpTreeVersions,
GroveDBOperationsDeleteVersions, GroveDBOperationsGetVersions,
GroveDBOperationsInsertVersions, GroveDBOperationsProofVersions,
GroveDBOperationsQueryVersions, GroveDBOperationsVersions,
GroveDBOperationsIndexedAxisVersions, GroveDBOperationsInsertVersions,
GroveDBOperationsProofVersions, GroveDBOperationsQueryVersions, GroveDBOperationsVersions,
GroveDBOperationsWorstCaseVersions, GroveDBPathQueryMethodVersions, GroveDBQueryLimits,
GroveDBReplicationVersions, GroveDBVersions,
},
Expand Down Expand Up @@ -150,6 +150,11 @@ pub const GROVE_V1: GroveVersion = GroveVersion {
follow_element: 0,
run_path_query: 0,
},
indexed_axis: GroveDBOperationsIndexedAxisVersions {
read: 0,
prove_single_path: 0,
verify_single_path: 0,
},
proof: GroveDBOperationsProofVersions {
prove_query: 0,
prove_query_many: 0,
Expand Down
9 changes: 7 additions & 2 deletions grovedb-version/src/version/v2.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,8 @@ use crate::version::{
GroveDBApplyBatchVersions, GroveDBElementMethodVersions,
GroveDBOperationsAverageCaseVersions, GroveDBOperationsDeleteUpTreeVersions,
GroveDBOperationsDeleteVersions, GroveDBOperationsGetVersions,
GroveDBOperationsInsertVersions, GroveDBOperationsProofVersions,
GroveDBOperationsQueryVersions, GroveDBOperationsVersions,
GroveDBOperationsIndexedAxisVersions, GroveDBOperationsInsertVersions,
GroveDBOperationsProofVersions, GroveDBOperationsQueryVersions, GroveDBOperationsVersions,
GroveDBOperationsWorstCaseVersions, GroveDBPathQueryMethodVersions, GroveDBQueryLimits,
GroveDBReplicationVersions, GroveDBVersions,
},
Expand Down Expand Up @@ -150,6 +150,11 @@ pub const GROVE_V2: GroveVersion = GroveVersion {
follow_element: 0,
run_path_query: 0,
},
indexed_axis: GroveDBOperationsIndexedAxisVersions {
read: 0,
prove_single_path: 0,
verify_single_path: 0,
},
proof: GroveDBOperationsProofVersions {
prove_query: 0,
prove_query_many: 0,
Expand Down
9 changes: 7 additions & 2 deletions grovedb-version/src/version/v3.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,8 @@ use crate::version::{
GroveDBApplyBatchVersions, GroveDBElementMethodVersions,
GroveDBOperationsAverageCaseVersions, GroveDBOperationsDeleteUpTreeVersions,
GroveDBOperationsDeleteVersions, GroveDBOperationsGetVersions,
GroveDBOperationsInsertVersions, GroveDBOperationsProofVersions,
GroveDBOperationsQueryVersions, GroveDBOperationsVersions,
GroveDBOperationsIndexedAxisVersions, GroveDBOperationsInsertVersions,
GroveDBOperationsProofVersions, GroveDBOperationsQueryVersions, GroveDBOperationsVersions,
GroveDBOperationsWorstCaseVersions, GroveDBPathQueryMethodVersions, GroveDBQueryLimits,
GroveDBReplicationVersions, GroveDBVersions,
},
Expand Down Expand Up @@ -154,6 +154,11 @@ pub const GROVE_V3: GroveVersion = GroveVersion {
follow_element: 0,
run_path_query: 0,
},
indexed_axis: GroveDBOperationsIndexedAxisVersions {
read: 0,
prove_single_path: 0,
verify_single_path: 0,
},
proof: GroveDBOperationsProofVersions {
prove_query: 0,
prove_query_many: 0,
Expand Down
18 changes: 15 additions & 3 deletions grovedb-version/src/version/v4.rs
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,13 @@
//! proved elements. V1..V3 refuse the shape on both sides. Gated because
//! it adds an acceptance rule to the live V1 envelope.
//!
//! - `path_query_methods.merge: 1` — `PathQuery::merge` requires every input
//! to agree on `left_to_right` (typed error on conflict) and propagates the
//! shared direction to the merged root. V1..V3 keep the long-standing
//! silent behavior (input directions dropped; sub-level merges take the
//! synthesized default). Gated because merged queries feed proofs and both
//! sides must re-derive the identical merged query.
//!
//! - `path_query_methods.unified_read_mode: 1` — `PathQuery` read modes
//! (axis-ordered and sum-budget reads carried in `Query::read_mode`) are
//! served by the unified dispatch (`run_path_query`, and the unified
Expand Down Expand Up @@ -77,8 +84,8 @@ use crate::version::{
GroveDBApplyBatchVersions, GroveDBElementMethodVersions,
GroveDBOperationsAverageCaseVersions, GroveDBOperationsDeleteUpTreeVersions,
GroveDBOperationsDeleteVersions, GroveDBOperationsGetVersions,
GroveDBOperationsInsertVersions, GroveDBOperationsProofVersions,
GroveDBOperationsQueryVersions, GroveDBOperationsVersions,
GroveDBOperationsIndexedAxisVersions, GroveDBOperationsInsertVersions,
GroveDBOperationsProofVersions, GroveDBOperationsQueryVersions, GroveDBOperationsVersions,
GroveDBOperationsWorstCaseVersions, GroveDBPathQueryMethodVersions, GroveDBQueryLimits,
GroveDBReplicationVersions, GroveDBVersions,
},
Expand Down Expand Up @@ -227,6 +234,11 @@ pub const GROVE_V4: GroveVersion = GroveVersion {
follow_element: 0,
run_path_query: 0,
},
indexed_axis: GroveDBOperationsIndexedAxisVersions {
read: 0,
prove_single_path: 0,
verify_single_path: 0,
},
proof: GroveDBOperationsProofVersions {
prove_query: 0,
prove_query_many: 0,
Expand Down Expand Up @@ -281,7 +293,7 @@ pub const GROVE_V4: GroveVersion = GroveVersion {
aggregate_sum_path_query_methods: GroveDBAggregateSumPathQueryMethodVersions { merge: 0 },
path_query_methods: GroveDBPathQueryMethodVersions {
terminal_keys: 0,
merge: 0,
merge: 1, // direction-aware merge: agreement required and propagated (V4+)
query_items_at_path: 0,
should_add_parent_tree_at_path: 0,
unified_read_mode: 1,
Expand Down
44 changes: 44 additions & 0 deletions grovedb/src/operations/indexed_tree.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1260,6 +1260,10 @@ impl GroveDb {
where
B: AsRef<[u8]> + 'b,
{
grovedb_version::check_grovedb_v0_with_cost!(
"indexed_axis_top_k_generic",
grove_version.grovedb_versions.operations.indexed_axis.read
);
let mut cost = OperationCost::default();
let tx = TxRef::new(&self.db, transaction);
let tx_ref = tx.as_ref();
Expand Down Expand Up @@ -1308,6 +1312,10 @@ impl GroveDb {
where
B: AsRef<[u8]> + 'b,
{
grovedb_version::check_grovedb_v0_with_cost!(
"indexed_axis_top_k_paginated_generic",
grove_version.grovedb_versions.operations.indexed_axis.read
);
let mut cost = OperationCost::default();
let tx = TxRef::new(&self.db, transaction);
let tx_ref = tx.as_ref();
Expand Down Expand Up @@ -1378,6 +1386,10 @@ impl GroveDb {
where
B: AsRef<[u8]> + 'b,
{
grovedb_version::check_grovedb_v0_with_cost!(
Comment thread
QuantumExplorer marked this conversation as resolved.
"indexed_axis_range_generic",
grove_version.grovedb_versions.operations.indexed_axis.read
);
let mut cost = OperationCost::default();
let tx = TxRef::new(&self.db, transaction);
let tx_ref = tx.as_ref();
Expand Down Expand Up @@ -1513,6 +1525,14 @@ impl GroveDb {
B: AsRef<[u8]> + 'b,
P: Into<SubtreePath<'b, B>>,
{
// The version gate has to precede the degenerate-range
// fast path below: returning the empty answer first would
// leave inverted bounds outside the version contract that
// every other input to this entry point is held to.
grovedb_version::check_grovedb_v0_with_cost!(
"indexed_count_range",
grove_version.grovedb_versions.operations.indexed_axis.read
);
let cost = OperationCost::default();
if lo_count > hi_count {
return Ok(Vec::new()).wrap_with_cost(cost);
Expand Down Expand Up @@ -1583,6 +1603,10 @@ impl GroveDb {
B: AsRef<[u8]> + 'b,
P: Into<SubtreePath<'b, B>>,
{
grovedb_version::check_grovedb_v0_with_cost!(
"indexed_count_range_aggregate",
grove_version.grovedb_versions.operations.indexed_axis.read
);
use grovedb_merk::proofs::query::QueryItem as MerkQueryItemForRange;

let mut cost = OperationCost::default();
Expand Down Expand Up @@ -1707,6 +1731,14 @@ impl GroveDb {
B: AsRef<[u8]> + 'b,
P: Into<SubtreePath<'b, B>>,
{
// The version gate has to precede the degenerate-range
// fast path below: returning the empty answer first would
// leave inverted bounds outside the version contract that
// every other input to this entry point is held to.
grovedb_version::check_grovedb_v0_with_cost!(
"indexed_sum_range",
grove_version.grovedb_versions.operations.indexed_axis.read
);
let cost = OperationCost::default();
if lo_sum > hi_sum {
return Ok(Vec::new()).wrap_with_cost(cost);
Expand Down Expand Up @@ -1769,6 +1801,10 @@ impl GroveDb {
B: AsRef<[u8]> + 'b,
P: Into<SubtreePath<'b, B>>,
{
grovedb_version::check_grovedb_v0_with_cost!(
"indexed_sum_range_aggregate",
grove_version.grovedb_versions.operations.indexed_axis.read
);
use grovedb_merk::proofs::query::QueryItem as MerkQueryItemForRange;

let mut cost = OperationCost::default();
Expand Down Expand Up @@ -1901,6 +1937,14 @@ impl GroveDb {
B: AsRef<[u8]> + 'b,
P: Into<SubtreePath<'b, B>>,
{
// The version gate has to precede the degenerate-range
// fast path below: returning the empty answer first would
// leave inverted bounds outside the version contract that
// every other input to this entry point is held to.
grovedb_version::check_grovedb_v0_with_cost!(
"indexed_avg_range",
grove_version.grovedb_versions.operations.indexed_axis.read
);
let cost = OperationCost::default();
if lo_avg > hi_avg {
return Ok(Vec::new()).wrap_with_cost(cost);
Expand Down
Loading
Loading