Skip to main content

query_planning/
mode.rs

1//! Aggregate and report execution-mode labels (wire DTOs).
2//!
3//! These labels describe aggregate evaluation contracts and preserved report
4//! vocabulary. They are not admission or product modes. The bounded resource
5//! controller owns the single bounded execution path. Result placement is
6//! carried by the plan's `ResultSink`. The plan-coverage modes
7//! (`native_required`, `native_preferred`, `functions_only`) described in
8//! `website/docs/design/native-lowering.mdx` classify DataFusion plan nodes
9//! and live in the adapter crate; nothing here selects between CPU and GPU.
10//!
11//! Every `as_str` value is a report and environment-variable contract. A
12//! label is renamed in place together with its consumers; `FromStr` accepts
13//! the short aliases only on input.
14
15use std::{fmt, str::FromStr};
16
17/// Execution contract for floating statistical aggregates such as
18/// `STDDEV_SAMP`, `STDDEV_POP`, `VAR_SAMP`, and `VAR_POP`.
19///
20/// [`Self::DataFusionExactHostWelford`] requires the supported DataFusion
21/// aggregate semantics and a validated input-order and evaluation contract.
22/// Bit-for-bit equivalence is claimed only for that contract. If the required
23/// contract is unavailable, planning rejects the native candidate, and a
24/// surrounding join or final sort does not supply exactness.
25/// [`Self::GpuNativeTolerant`] remains the default and does not promise
26/// bit-for-bit equivalence with DataFusion.
27#[derive(Debug, Default, Clone, Copy, PartialEq, Eq, Hash)]
28pub enum StatisticalAggregateExecutionMode {
29    /// Evaluate DataFusion's Welford accumulator on the host over the
30    /// aggregate input, in DataFusion input order where the plan carries it.
31    ///
32    /// Aggregates in the same spec keep DataFusion's integer and NaN
33    /// semantics. Planning rejects group-key and aggregate types that this
34    /// host kernel does not cover, as well as ungrouped floating `SUM` and `AVG`,
35    /// which DataFusion accumulates batch by batch. Those plans remain on
36    /// DataFusion. A NaN result matches as a NaN, with no bit-for-bit equality.
37    /// An add of two NaN operands keeps the sign of the operand the compiler
38    /// places first, in DataFusion and in this kernel alike.
39    DataFusionExactHostWelford,
40    /// Uses libcudf native grouped and scalar statistical kernels with each
41    /// aggregate's declared degrees of freedom. This mode is the default.
42    ///
43    /// Variance is derived from sum and sum-of-squares moments, while
44    /// DataFusion uses Welford updates. Floating-point results can differ from
45    /// DataFusion, and precision degrades when values are large relative to
46    /// their spread.
47    #[default]
48    GpuNativeTolerant,
49}
50
51impl StatisticalAggregateExecutionMode {
52    /// Stable snake-case mode label used in env vars and reports.
53    pub const fn as_str(self) -> &'static str {
54        match self {
55            Self::DataFusionExactHostWelford => "datafusion_exact_host_welford",
56            Self::GpuNativeTolerant => "gpu_native_tolerant",
57        }
58    }
59
60    /// Floating parity contract required by this execution mode.
61    pub const fn tolerance_contract(self) -> StatisticalAggregateToleranceContract {
62        match self {
63            Self::DataFusionExactHostWelford => StatisticalAggregateToleranceContract::Exact,
64            Self::GpuNativeTolerant => StatisticalAggregateToleranceContract::FloatTolerant,
65        }
66    }
67
68    /// Returns true when execution must preserve DataFusion's exact Welford bits.
69    ///
70    /// Admission reads this to decide whether a plan with floating statistical
71    /// aggregates is rejected for group-key or aggregate types the host kernel
72    /// does not cover; under the tolerant mode those plans run on libcudf.
73    pub const fn is_datafusion_exact(self) -> bool {
74        matches!(self, Self::DataFusionExactHostWelford)
75    }
76}
77
78impl fmt::Display for StatisticalAggregateExecutionMode {
79    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
80        f.write_str(self.as_str())
81    }
82}
83
84impl FromStr for StatisticalAggregateExecutionMode {
85    type Err = StatisticalAggregateExecutionModeParseError;
86
87    fn from_str(value: &str) -> Result<Self, Self::Err> {
88        match value.to_ascii_lowercase().as_str() {
89            "datafusion_exact_host_welford" | "datafusion_exact" | "exact" => Ok(Self::DataFusionExactHostWelford),
90            "gpu_native_tolerant" | "gpu_native" | "tolerant" => Ok(Self::GpuNativeTolerant),
91            other => Err(StatisticalAggregateExecutionModeParseError { value: other.to_string() }),
92        }
93    }
94}
95
96/// Error returned when parsing a statistical aggregate execution-mode label
97/// fails.
98#[derive(Debug, Clone, PartialEq, Eq)]
99pub struct StatisticalAggregateExecutionModeParseError {
100    value: String,
101}
102
103impl fmt::Display for StatisticalAggregateExecutionModeParseError {
104    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
105        write!(f, "invalid statistical aggregate execution mode: {}", self.value)
106    }
107}
108
109impl std::error::Error for StatisticalAggregateExecutionModeParseError {}
110
111/// Tolerance contract declared by a floating statistical aggregate execution
112/// mode.
113#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
114pub enum StatisticalAggregateToleranceContract {
115    /// Results are expected to be identical to the compatibility contract.
116    Exact,
117    /// Results are floating-point equivalent under a declared tolerance.
118    FloatTolerant,
119}
120
121impl StatisticalAggregateToleranceContract {
122    /// Stable snake-case tolerance-contract label used in reports.
123    pub const fn as_str(self) -> &'static str {
124        match self {
125            Self::Exact => "exact",
126            Self::FloatTolerant => "float_tolerant",
127        }
128    }
129}
130
131/// Admission status used by capability reports.
132///
133/// The single bounded controller owns admission. The `Display` implementation
134/// emits the stable serialized wire labels, which predate the capability-first
135/// vocabulary: `Admitted` serializes as `bounded` and `Rejected` as
136/// `unsupported_bounded`.
137#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
138pub enum CapabilityEnvelopeStatus {
139    /// The plan is ready for bounded execution.
140    Admitted,
141    /// At least one capability decision blocks bounded execution.
142    Rejected,
143}
144
145impl CapabilityEnvelopeStatus {
146    /// Stable wire label emitted by report serializers.
147    pub(crate) const fn as_str(self) -> &'static str {
148        match self {
149            Self::Admitted => "bounded",
150            Self::Rejected => "unsupported_bounded",
151        }
152    }
153}
154
155impl fmt::Display for CapabilityEnvelopeStatus {
156    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
157        f.write_str(self.as_str())
158    }
159}