Skip to main content

drive/query/drive_document_ranked_query/mode_detection/
mod.rs

1//! Request-shape validation for the ranked query, and the versioned
2//! `(select, group_by, order_by, limit, offset)` → [`DocumentRankedMode`]
3//! resolution.
4//!
5//! Pure functions on the request shape — no Drive, no contract, no
6//! indexes. Available under `server` (the dispatcher validates before
7//! executing) and `verify` (the SDK validates the same way before
8//! attempting proof verification), so both sides agree on which requests
9//! are well-formed and on the `(axis, descending, k, offset)` tuple a
10//! well-formed one resolves to. Index-dependent validation ("does an
11//! index actually cover this axis?") needs the document type's index map
12//! and lives in [`super::index_picker`].
13//!
14//! Versioned through
15//! `platform_version.drive.methods.document.query.detect_ranked_mode`,
16//! the same way
17//! [`DriveDocumentCountQuery::detect_mode_versioned`](super::super::drive_document_count_query::DriveDocumentCountQuery::detect_mode_versioned)
18//! routes count's table: the accepted request grammar is a consensus-
19//! adjacent contract on the query surface, so relaxing it later has to
20//! land behind a method-version bump rather than changing what an
21//! already-deployed protocol version accepts.
22
23use super::{
24    DocumentRankedMode, RankedAxis, RankedPaginationInputs, MAX_RANKED_LIMIT,
25    RANKED_COUNT_ORDER_KEY,
26};
27use crate::error::query::QuerySyntaxError;
28use crate::error::Error;
29use crate::query::having::HavingClause;
30use crate::query::projection::{SelectFunction, SelectProjection};
31use crate::query::{OrderClause, WhereClause};
32use dpp::version::PlatformVersion;
33
34/// Versioned entry point. Routes through
35/// `platform_version.drive.methods.document.query.detect_ranked_mode`;
36/// today only `0` is defined and maps to [`detect_ranked_mode_v0`]
37/// verbatim.
38pub fn detect_ranked_mode(
39    select: &SelectProjection,
40    group_by: &[String],
41    having: &[HavingClause],
42    order_by: &[OrderClause],
43    where_clauses: &[WhereClause],
44    pagination: RankedPaginationInputs,
45    platform_version: &PlatformVersion,
46) -> Result<DocumentRankedMode, Error> {
47    match platform_version
48        .drive
49        .methods
50        .document
51        .query
52        .detect_ranked_mode
53    {
54        0 => detect_ranked_mode_v0(
55            select,
56            group_by,
57            having,
58            order_by,
59            where_clauses,
60            pagination,
61        ),
62        version => Err(Error::Query(QuerySyntaxError::Unsupported(format!(
63            "detect_ranked_mode: unknown method version {version}; only 0 is supported"
64        )))),
65    }
66}
67
68/// The `ORDER BY` field name that names a given select's aggregate.
69///
70/// `SUM(f)` / `AVG(f)` are ordered by naming `f` — the same field the
71/// projection aggregates, which is how SQL's `ORDER BY avg(grade)`
72/// reads once the aggregate function is already fixed by the `SELECT`.
73/// `COUNT(*)` has no field, so it is named by the
74/// [`RANKED_COUNT_ORDER_KEY`] sentinel.
75///
76/// Public because request *builders* need it as much as the validator
77/// does: an SDK offering `.order_by_selected_aggregate(…)` has to emit
78/// the same string this function expects to read back, and a second
79/// copy of the sentinel rule is a silent-rejection bug waiting for the
80/// first `COUNT(*)` ranking.
81pub fn ranked_order_key(select: &SelectProjection) -> &str {
82    match select.function {
83        SelectFunction::Count if select.field.is_empty() => RANKED_COUNT_ORDER_KEY,
84        _ => select.field.as_str(),
85    }
86}
87
88mod v0;
89// Re-exported so the dispatcher's callers (`drive_dispatcher`, the
90// test suites) keep addressing the frozen grammar by its old path.
91pub use v0::{detect_ranked_mode_v0, prefix_pins_from_where_clauses};