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};