From 5687dce7d77e65557c556b34a5285d29125b160d Mon Sep 17 00:00:00 2001 From: Quantum Explorer Date: Thu, 21 May 2026 06:34:26 +0700 Subject: [PATCH] Preserve subquery limit after offset skips --- grovedb-version/src/version/v3.rs | 2 +- grovedb/src/element/query.rs | 16 +++--- grovedb/src/tests/query_tests.rs | 81 +++++++++++++++++++++++++++++++ 3 files changed, 92 insertions(+), 7 deletions(-) diff --git a/grovedb-version/src/version/v3.rs b/grovedb-version/src/version/v3.rs index 039891bd1..cda0cf286 100644 --- a/grovedb-version/src/version/v3.rs +++ b/grovedb-version/src/version/v3.rs @@ -66,7 +66,7 @@ pub const GROVE_V3: GroveVersion = GroveVersion { get_path_query: 0, get_sized_query: 0, get_aggregate_sum_query_apply_function: 0, - path_query_push: 0, + path_query_push: 1, aggregate_sum_path_query_push: 0, query_item: 0, basic_push: 0, diff --git a/grovedb/src/element/query.rs b/grovedb/src/element/query.rs index 1584abd4d..0e7371ed0 100644 --- a/grovedb/src/element/query.rs +++ b/grovedb/src/element/query.rs @@ -12,7 +12,10 @@ use grovedb_merk::{ }; use grovedb_path::SubtreePath; use grovedb_storage::{rocksdb_storage::RocksDbStorage, RawIterator, StorageContext}; -use grovedb_version::{check_grovedb_v0, check_grovedb_v0_with_cost, version::GroveVersion}; +use grovedb_version::{ + check_grovedb_v0, check_grovedb_v0_or_v1_with_cost, check_grovedb_v0_with_cost, + version::GroveVersion, +}; use crate::{ element::{path_query_push_args::PathQueryPushArgs, query_options::QueryOptions}, @@ -331,10 +334,8 @@ impl ElementQueryExtensions for Element { ) -> CostResult<(), Error> { use crate::util::{compat, TxRef}; - check_grovedb_v0_with_cost!( - "path_query_push", - grove_version.grovedb_versions.element.path_query_push - ); + let path_query_push_version = grove_version.grovedb_versions.element.path_query_push; + check_grovedb_v0_or_v1_with_cost!("path_query_push", path_query_push_version); // println!("path_query_push {} \n", args); @@ -396,7 +397,10 @@ impl ElementQueryExtensions for Element { ); if let Some(limit) = limit { - if sub_elements.is_empty() && decrease_limit_on_range_with_no_sub_elements { + if sub_elements.is_empty() + && decrease_limit_on_range_with_no_sub_elements + && (path_query_push_version == 0 || skipped == 0) + { // we should decrease by 1 in this case *limit = limit.saturating_sub(1); } else { diff --git a/grovedb/src/tests/query_tests.rs b/grovedb/src/tests/query_tests.rs index 702f8b7cf..31192b72a 100644 --- a/grovedb/src/tests/query_tests.rs +++ b/grovedb/src/tests/query_tests.rs @@ -873,6 +873,87 @@ mod tests { compare_result_sets(&elements, &result_set); } + #[test] + fn test_subquery_offset_consumed_empty_does_not_decrease_limit_forward() { + let grove_version = GroveVersion::latest(); + let db = make_test_grovedb(grove_version); + populate_tree_for_unique_range_subquery(&db, grove_version); + + let path = vec![TEST_LEAF.to_vec()]; + let mut query = Query::new_with_direction(true); + query.insert_range(1988_u32.to_be_bytes().to_vec()..1992_u32.to_be_bytes().to_vec()); + query.set_subquery(Query::new_range_full()); + + let path_query = PathQuery::new(path, SizedQuery::new(query, Some(2), Some(1))); + + let (elements, skipped) = db + .query_item_value(&path_query, true, true, true, None, grove_version) + .unwrap() + .expect("expected successful get_path_query"); + + assert_eq!(skipped, 1); + assert_eq!( + elements, + vec![ + 1989_u32.to_be_bytes().to_vec(), + 1990_u32.to_be_bytes().to_vec() + ] + ); + } + + #[test] + fn test_subquery_offset_consumed_empty_does_not_decrease_limit_reverse() { + let grove_version = GroveVersion::latest(); + let db = make_test_grovedb(grove_version); + populate_tree_for_unique_range_subquery(&db, grove_version); + + let path = vec![TEST_LEAF.to_vec()]; + let mut query = Query::new_with_direction(false); + query.insert_range(1988_u32.to_be_bytes().to_vec()..1992_u32.to_be_bytes().to_vec()); + query.set_subquery(Query::new_range_full()); + + let path_query = PathQuery::new(path, SizedQuery::new(query, Some(2), Some(1))); + + let (elements, skipped) = db + .query_item_value(&path_query, true, true, true, None, grove_version) + .unwrap() + .expect("expected successful get_path_query"); + + assert_eq!(skipped, 1); + assert_eq!( + elements, + vec![ + 1990_u32.to_be_bytes().to_vec(), + 1989_u32.to_be_bytes().to_vec() + ] + ); + } + + #[test] + fn test_subquery_offset_consumed_empty_legacy_version_decreases_limit() { + let mut legacy_version = GroveVersion::latest().clone(); + legacy_version.grovedb_versions.element.path_query_push = 0; + let grove_version = &legacy_version; + + let db = make_test_grovedb(grove_version); + populate_tree_for_unique_range_subquery(&db, grove_version); + + let path = vec![TEST_LEAF.to_vec()]; + let mut query = Query::new_with_direction(true); + query.insert_range(1988_u32.to_be_bytes().to_vec()..1992_u32.to_be_bytes().to_vec()); + query.set_subquery(Query::new_range_full()); + + let path_query = PathQuery::new(path, SizedQuery::new(query, Some(2), Some(1))); + + let (elements, skipped) = db + .query_item_value(&path_query, true, true, true, None, grove_version) + .unwrap() + .expect("expected successful get_path_query"); + + assert_eq!(skipped, 1); + assert_eq!(elements, vec![1989_u32.to_be_bytes().to_vec()]); + } + #[test] fn test_get_range_query_with_unique_subquery_on_references() { let grove_version = GroveVersion::latest();