Skip to content
Merged
Show file tree
Hide file tree
Changes from 4 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
Original file line number Diff line number Diff line change
Expand Up @@ -5,29 +5,32 @@
//! against the v1 query surface is executed: as one of the four
//! `(group_by × where)` grouped modes, or — from feature version 1 —
//! as a *ranked* request when the request orders by the aggregate it
//! selects (`GROUP BY p ORDER BY <the selected aggregate>`). Routing
//! here only asks whether the `order_by` names that aggregate; the
//! direction, the limit's bounds and the offset are drive's call and
//! are made in `detect_ranked_mode`. It also enforces the per-mode
//! `accepts_limit()` contract on the grouped path.
//! selects (`GROUP BY p ORDER BY <the selected aggregate>`), or — from
//! feature version 2 — as a *having-range* request when a grouped
//! aggregate carries exactly one `having` clause. Routing here only
//! asks which shape the request has; the direction, the bounds, the
//! limit's contract and the offset are drive's call and are made in
//! `detect_ranked_mode` / `detect_having_mode`. It also enforces the
//! per-mode `accepts_limit()` contract on the grouped path.
//!
//! The routing rules it embeds are part of the query contract clients
//! see on the wire — a change to which `(group_by × where_clauses ×
//! order_by)` shapes are accepted becomes consensus-visible because the
//! dispatcher runs on every v1 query request. Versioning it lets later
//! protocol bumps adjust the routing table without breaking older
//! nodes' replay of historical traffic, and is what keeps a
//! mixed-version network in agreement across the ranked-query
//! activation: protocol version 13 and earlier select v0, which has no
//! ranked path at all, while protocol version 14 selects v1 and answers
//! ranked queries.
//! mixed-version network in agreement across the ranked-query and
//! having-range activations: protocol version 13 and earlier select v0,
//! which has neither path, while protocol version 14 selects v2 and
//! answers both.
//!
//! Lives next to the v1 query handler (the only call site today) and
//! is dispatched via the `DriveAbciDocumentQueryHelperVersions` slot
//! in `PlatformVersion`.

mod v0;
mod v1;
mod v2;

use crate::error::query::QueryError;
use dpp::version::PlatformVersion;
Expand Down Expand Up @@ -56,6 +59,15 @@ pub(super) enum AggregateRouting {
Grouped(CountMode),
/// Ranked aggregate: execute through the ranked (top-k) surface.
Ranked,
/// Boolean-`HAVING` range: a grouped aggregate carrying exactly one
/// `having` clause, executed through
/// `Drive::execute_document_having_request` as a value-bounded range
/// read of the covering ranked index's axis secondary. The
/// feature-version-2 addition. Carries no data for the same reason
/// `Ranked` carries none: drive owns the having grammar
/// (`detect_having_mode`), and routing only decides *where* the
/// request goes.
HavingRange,
}

/// Decide how a `SELECT COUNT` / `SUM` / `AVG` request executes.
Expand Down Expand Up @@ -113,10 +125,19 @@ pub(super) fn compute_aggregate_mode_and_check_limit(
having,
function_name,
),
2 => v2::compute_aggregate_mode_and_check_limit_v2(
select,
group_by,
where_clauses,
order_by,
limit,
having,
function_name,
),
version => Err(QueryError::Drive(drive::error::Error::Drive(
drive::error::drive::DriveError::UnknownVersionMismatch {
method: "compute_aggregate_mode_and_check_limit".to_string(),
known_versions: vec![0, 1],
known_versions: vec![0, 1, 2],
received: version,
},
))),
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
//! Feature version 2 of the aggregate routing helper: the boolean-`HAVING`
//! range activation.
//!
//! Differs from v1 in exactly one branch: a grouped aggregate carrying a
//! **single** `having` clause routes to the having-range executor
//! ([`AggregateRouting::HavingRange`]) instead of being refused. Routing
//! here only asks "is there a grouped select with exactly one having
//! clause?" — whether the clause bounds the selected aggregate, whether
//! its operator translates to a contiguous range, whether an `order_by`
//! is compatible, and the limit's bounds are all drive's call, made in
//! `detect_having_mode`; a routing-layer copy of that grammar could
//! disagree with drive's after a version bump.
//!
//! Multi-clause `having` (implicit AND) keeps the `not_yet_implemented`
//! contract: each extra clause needs a per-candidate post-check against
//! the primary that no executor performs yet. And `having` without
//! `group_by` still falls through to the v1 → v0 blanket rejection — a
//! global aggregate produces one row, and bounding it is a client-side
//! comparison, not a query.

use super::v1::compute_aggregate_mode_and_check_limit_v1;
use super::AggregateRouting;
use crate::error::query::QueryError;
use crate::query::document_query::v1::not_yet_implemented;
use drive::query::{HavingClause, OrderClause, SelectProjection, WhereClause};

#[allow(clippy::too_many_arguments)]
pub(super) fn compute_aggregate_mode_and_check_limit_v2(
select: &SelectProjection,
group_by: &[String],
where_clauses: &[WhereClause],
order_by: &[OrderClause],
limit: Option<u32>,
having: &[HavingClause],
function_name: &str,
) -> Result<AggregateRouting, QueryError> {
if !having.is_empty() && !group_by.is_empty() {
return match having {
[_single] => Ok(AggregateRouting::HavingRange),
many => Err(not_yet_implemented(&format!(
"multiple HAVING clauses (implicit AND): got {}. One clause on the \
selected {function_name} aggregate is served as a single contiguous \
range read of the covering ranked index's axis secondary; additional \
clauses would need a per-candidate post-check that is not implemented. \
Narrow to a single clause",
many.len()
))),
};
}

// No having (or no group_by, where a having still dies in v0's
// blanket rejection): identical routing to v1, including its ranked
// detection and its delegation to v0 for non-ranked shapes.
compute_aggregate_mode_and_check_limit_v1(
select,
group_by,
where_clauses,
order_by,
limit,
having,
function_name,
)
}
Loading
Loading