mz_repr/optimize.rs
1// Copyright Materialize, Inc. and contributors. All rights reserved.
2//
3// Use of this software is governed by the Business Source License
4// included in the LICENSE file.
5//
6// As of the Change Date specified in that file, in accordance with
7// the Business Source License, use of this software will be governed
8// by the Apache License, Version 2.0.
9
10//! Facilities for defining optimizer feature flags.
11
12use std::collections::BTreeMap;
13
14#[cfg(any(test, feature = "proptest"))]
15use proptest_derive::Arbitrary;
16use serde::{Deserialize, Serialize};
17
18/// A macro for feature flags managed by the optimizer.
19macro_rules! optimizer_feature_flags {
20 ({ $($feature:ident: $type:ty,)* }) => {
21 #[derive(Clone, Debug, Default, PartialEq, Eq, Serialize, Deserialize)]
22 #[cfg_attr(any(test, feature = "proptest"), derive(Arbitrary))]
23 pub struct OptimizerFeatures {
24 $(pub $feature: $type),*
25 }
26
27 #[derive(Clone, Debug, Deserialize, Serialize, PartialOrd, PartialEq, Eq, Ord)]
28 pub struct OptimizerFeatureOverrides {
29 $(pub $feature: Option<$type>),*
30 }
31
32 impl Default for OptimizerFeatureOverrides {
33 fn default() -> Self {
34 Self {
35 $($feature: None),*
36 }
37 }
38 }
39
40 /// An [`OverrideFrom`] implementation that updates
41 /// [`OptimizerFeatures`] using [`OptimizerFeatureOverrides`] values.
42 impl OverrideFrom<OptimizerFeatureOverrides> for OptimizerFeatures {
43 fn override_from(mut self, overrides: &OptimizerFeatureOverrides) -> Self {
44 $(if let Some(feature_value) = overrides.$feature {
45 self.$feature = feature_value;
46 })*
47 self
48 }
49 }
50
51 /// An `OptimizerFeatureOverrides ⇒ BTreeMap<String, String>`
52 /// conversion.
53 ///
54 /// WARNING: changing the definition of item might break catalog
55 /// re-hydration for some catalog items (such as entries for `CREATE
56 /// CLUSTER ... FEATURES(...)` statements).
57 impl From<BTreeMap<String, String>> for OptimizerFeatureOverrides {
58 fn from(value: BTreeMap<String, String>) -> Self {
59 let mut overrides = OptimizerFeatureOverrides::default();
60
61 for (name, value) in value.into_iter() {
62 match name.as_str() {
63 $(stringify!($feature) => {
64 let value = Some(<$type>::decode(&value));
65 overrides.$feature = value;
66 }),*
67 _ => (), // Ignore
68 }
69 }
70
71 overrides
72 }
73 }
74
75 /// A `BTreeMap<String, String> ⇒ OptimizerFeatureOverrides` conversion.
76 ///
77 /// WARNING: changing the definition of item might break catalog
78 /// re-hydration for some catalog items (such as entries for `CREATE
79 /// CLUSTER ... FEATURES(...)` statements).
80 impl From<OptimizerFeatureOverrides> for BTreeMap<String, String> {
81 fn from(overrides: OptimizerFeatureOverrides) -> Self {
82 let mut map = BTreeMap::<String, String>::default();
83
84 $(if let Some(value) = overrides.$feature {
85 let k = stringify!($feature).into();
86 let v = value.encode();
87 map.insert(k, v);
88 };)*
89
90 map
91 }
92 }
93 };
94}
95
96optimizer_feature_flags!({
97 // Use `EquivalenceClassesWithholdingErrors` instead of raw
98 // `EquivalenceClasses` during eq prop for joins.
99 enable_eq_classes_withholding_errors: bool,
100 // Bound from `SystemVars::enable_eager_delta_joins`.
101 enable_eager_delta_joins: bool,
102 // Enable Lattice-based fixpoint iteration on LetRec nodes in the
103 // Analysis framework.
104 enable_letrec_fixpoint_analysis: bool,
105 // Bound from `SystemVars::enable_new_outer_join_lowering`.
106 enable_new_outer_join_lowering: bool,
107 // Bound from `SystemVars::enable_reduce_mfp_fusion`.
108 enable_reduce_mfp_fusion: bool,
109 // Enable joint HIR ⇒ MIR lowering of stacks of left joins.
110 enable_variadic_left_join_lowering: bool,
111 // Enable cardinality estimation
112 enable_cardinality_estimates: bool,
113 // An exclusive upper bound on the number of results we may return from a
114 // Persist fast-path peek. Required by the `create_fast_path_plan` call in
115 // `peek::Optimizer`.
116 persist_fast_path_limit: usize,
117 // Reoptimize imported views when building and optimizing a
118 // `DataflowDescription` in the global MIR optimization phase.
119 reoptimize_imported_views: bool,
120 // See the feature flag of the same name.
121 enable_join_prioritize_arranged: bool,
122 // See the feature flag of the same name.
123 enable_projection_pushdown_after_relation_cse: bool,
124 // See the feature flag of the same name.
125 enable_union_cancellation_after_relation_cse: bool,
126 // See the feature flag of the same name.
127 enable_less_reduce_in_eqprop: bool,
128 // See the feature flag of the same name.
129 enable_dequadratic_eqprop_map: bool,
130 // See the feature flag of the same name.
131 enable_fast_path_plan_insights: bool,
132 // See the feature flag of the same name.
133 enable_cast_elimination: bool,
134 // See the feature flag of the same name.
135 enable_case_literal_transform: bool,
136 // See the feature flag of the same name.
137 enable_simplify_quantified_comparisons: bool,
138 // See the feature flag of the same name.
139 enable_simplify_from_less_existence: bool,
140 // See the feature flag of the same name.
141 enable_coalesce_case_transform: bool,
142 // See the feature flag of the same name.
143 enable_will_distinct_propagation: bool,
144 // See the feature flag of the same name.
145 enable_fixed_correlated_cte_lowering: bool,
146});
147
148/// A trait used to implement layered config construction.
149pub trait OverrideFrom<T> {
150 /// Override the configuration represented by [`Self`] with values
151 /// from the given `layer`.
152 fn override_from(self, layer: &T) -> Self;
153}
154
155/// Overrides for `U` coming from an optional `T`.
156impl<T, U> OverrideFrom<Option<&T>> for U
157where
158 Self: OverrideFrom<T>,
159{
160 fn override_from(self, layer: &Option<&T>) -> Self {
161 match layer {
162 Some(layer) => self.override_from(layer),
163 None => self,
164 }
165 }
166}
167
168/// A trait that handles conversion of feature flags.
169trait OptimizerFeatureType {
170 fn encode(self) -> String;
171 fn decode(v: &str) -> Self;
172}
173
174/// A macro that implements [`OptimizerFeatureType`] for most common types.
175///
176/// WARNING: changing the definition of this macro might break catalog
177/// re-hydration for some catalog items (such as entries for `CREATE CLUSTER ...
178/// FEATURES(...)` statements).
179macro_rules! impl_optimizer_feature_type {
180 ($($type:ty),*) => {
181 $(
182 impl OptimizerFeatureType for $type {
183 fn encode(self) -> String {
184 self.to_string()
185 }
186
187 fn decode(v: &str) -> Self {
188 str::parse(&v).unwrap()
189 }
190 }
191 )*
192 };
193}
194
195// Implement `OptimizerFeatureType` for all types used in the
196// `optimizer_feature_flags!(...)` call above.
197impl_optimizer_feature_type![usize];
198
199impl OptimizerFeatureType for bool {
200 fn encode(self) -> String {
201 self.to_string()
202 }
203
204 fn decode(v: &str) -> Self {
205 // Accept both the Rust spelling (`true`/`false`, what `encode` and
206 // `CREATE CLUSTER ... FEATURES` produce) and the canonical system-var /
207 // PostgreSQL spellings (`on`/`off`, ...). A cluster-coherent scoped
208 // override may arrive as the var's canonical `on`/`off`.
209 match v.trim().to_lowercase().as_str() {
210 "t" | "true" | "on" | "1" | "y" | "yes" => true,
211 "f" | "false" | "off" | "0" | "n" | "no" => false,
212 // The writer only stores values that passed `canonicalize`, so this
213 // arm is unreachable in practice. Log rather than panic anyway: a
214 // stored bad value reaching the plan path would otherwise crash the
215 // optimizer on every query for the affected cluster. Fall back to
216 // `false`.
217 other => {
218 mz_ore::soft_panic_or_log!("invalid boolean optimizer feature override: {other:?}");
219 false
220 }
221 }
222 }
223}
224
225#[cfg(test)]
226mod tests {
227 use std::collections::BTreeMap;
228
229 use super::OptimizerFeatureOverrides;
230
231 #[mz_ore::test]
232 fn bool_override_decodes_lenient_spellings() {
233 for (stored, want) in [
234 ("true", true),
235 ("false", false),
236 ("on", true),
237 ("off", false),
238 ] {
239 let map =
240 BTreeMap::from([("enable_eager_delta_joins".to_string(), stored.to_string())]);
241 assert_eq!(
242 OptimizerFeatureOverrides::from(map).enable_eager_delta_joins,
243 Some(want),
244 );
245 }
246 }
247}