Skip to main content

mz_sql/session/
vars.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//! Run-time configuration parameters
11//!
12//! ## Overview
13//! Materialize roughly follows the PostgreSQL configuration model, which works
14//! as follows. There is a global set of named configuration parameters, like
15//! `DateStyle` and `client_encoding`. These parameters can be set in several
16//! places: in an on-disk configuration file (in Postgres, named
17//! postgresql.conf), in command line arguments when the server is started, or
18//! at runtime via the `ALTER SYSTEM` or `SET` statements. Parameters that are
19//! set in a session take precedence over database defaults, which in turn take
20//! precedence over command line arguments, which in turn take precedence over
21//! settings in the on-disk configuration. Note that changing the value of
22//! parameters obeys transaction semantics: if a transaction fails to commit,
23//! any parameters that were changed in that transaction (i.e., via `SET`) will
24//! be rolled back to their previous value.
25//!
26//! The Materialize configuration hierarchy at the moment is much simpler.
27//! Global defaults are hardcoded into the binary, and a select few parameters
28//! can be overridden per session. A select few parameters can be overridden on
29//! disk.
30//!
31//! The set of variables that can be overridden per session and the set of
32//! variables that can be overridden on disk are currently disjoint. The
33//! infrastructure has been designed with an eye towards merging these two sets
34//! and supporting additional layers to the hierarchy, however, should the need
35//! arise.
36//!
37//! The configuration parameters that exist are driven by compatibility with
38//! PostgreSQL drivers that expect them, not because they are particularly
39//! important.
40//!
41//! ## Structure
42//! The most meaningful exports from this module are:
43//!
44//! - [`SessionVars`] represent per-session parameters, which each user can
45//!   access independently of one another, and are accessed via `SET`.
46//!
47//!   The fields of [`SessionVars`] are either;
48//!     - `SessionVar`, which is preferable and simply requires full support of
49//!       the `SessionVar` impl for its embedded value type.
50//!     - `ServerVar` for types that do not currently support everything
51//!       required by `SessionVar`, e.g. they are fixed-value parameters.
52//!
53//!   In the fullness of time, all fields in [`SessionVars`] should be
54//!   `SessionVar`.
55//!
56//! - [`SystemVars`] represent system-wide configuration settings and are
57//!   accessed via `ALTER SYSTEM SET`.
58//!
59//!   All elements of [`SystemVars`] are `SystemVar`.
60//!
61//! Some [`VarDefinition`] are also marked as a [`FeatureFlag`]; this is just a
62//! wrapper to make working with a set of [`VarDefinition`] easier, primarily from
63//! within SQL planning, where we might want to check if a feature is enabled
64//! before planning it.
65
66use std::borrow::Cow;
67use std::clone::Clone;
68use std::collections::BTreeMap;
69use std::fmt::Debug;
70use std::net::IpAddr;
71use std::num::NonZeroU32;
72use std::string::ToString;
73use std::sync::{Arc, LazyLock};
74use std::time::Duration;
75
76use chrono::{DateTime, Utc};
77use derivative::Derivative;
78use imbl::OrdMap;
79use mz_build_info::BuildInfo;
80use mz_dyncfg::{ConfigSet, ConfigType, ConfigUpdates, ConfigVal, ParameterScope};
81use mz_persist_client::cfg::{
82    CRDB_CONNECT_TIMEOUT, CRDB_KEEPALIVES_IDLE, CRDB_KEEPALIVES_INTERVAL, CRDB_KEEPALIVES_RETRIES,
83    CRDB_TCP_USER_TIMEOUT,
84};
85use mz_pgrepr::TextEncodeSettings;
86use mz_repr::adt::numeric::Numeric;
87use mz_repr::adt::timestamp::CheckedTimestamp;
88use mz_repr::bytes::ByteSize;
89use mz_repr::user::{ExternalUserMetadata, InternalUserMetadata};
90use mz_tracing::{CloneableEnvFilter, SerializableDirective};
91use serde::Serialize;
92use thiserror::Error;
93use uncased::UncasedStr;
94
95use crate::ast::Ident;
96use crate::session::user::User;
97
98pub(crate) mod constraints;
99pub(crate) mod definitions;
100pub(crate) mod errors;
101pub(crate) mod polyfill;
102pub(crate) mod value;
103
104pub use definitions::*;
105pub use errors::*;
106pub use value::*;
107
108/// The action to take during end_transaction.
109///
110/// This enum lives here because of convenience: it's more of an adapter
111/// concept but [`SessionVars::end_transaction`] takes it.
112#[derive(Debug, Clone, Copy, PartialEq, Eq)]
113pub enum EndTransactionAction {
114    /// Commit the transaction.
115    Commit,
116    /// Rollback the transaction.
117    Rollback,
118}
119
120/// Represents the input to a variable.
121///
122/// Each variable has different rules for how it handles each style of input.
123/// This type allows us to defer interpretation of the input until the
124/// variable-specific interpretation can be applied.
125#[derive(Debug, Clone, Copy)]
126pub enum VarInput<'a> {
127    /// The input has been flattened into a single string.
128    ///
129    /// NOTE: when adding a new variant here (or in [`OwnedVarInput`]), extend
130    /// the `mz_catalog.mz_role_parameters` materialized view in
131    /// `src/catalog/src/builtin/mz_catalog.rs`. That MV discriminates on the
132    /// externally-tagged JSON shape of [`OwnedVarInput`] to format
133    /// `parameter_value`.
134    Flat(&'a str),
135    /// The input comes from a SQL `SET` statement and is jumbled across
136    /// multiple components.
137    ///
138    /// NOTE: see the doc-comment on [`VarInput::Flat`] — adding a new variant
139    /// requires extending `mz_catalog.mz_role_parameters`.
140    SqlSet(&'a [String]),
141}
142
143impl<'a> VarInput<'a> {
144    /// Converts the variable input to an owned vector of strings.
145    pub fn to_vec(&self) -> Vec<String> {
146        match self {
147            VarInput::Flat(v) => vec![v.to_string()],
148            VarInput::SqlSet(values) => values.into_iter().map(|v| v.to_string()).collect(),
149        }
150    }
151}
152
153/// An owned version of [`VarInput`].
154#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Serialize)]
155pub enum OwnedVarInput {
156    /// See [`VarInput::Flat`].
157    ///
158    /// NOTE: adding a new variant requires extending the
159    /// `mz_catalog.mz_role_parameters` materialized view in
160    /// `src/catalog/src/builtin/mz_catalog.rs`, which discriminates on the
161    /// externally-tagged JSON shape of this enum.
162    Flat(String),
163    /// See [`VarInput::SqlSet`].
164    ///
165    /// NOTE: see the doc-comment on [`OwnedVarInput::Flat`].
166    SqlSet(Vec<String>),
167}
168
169impl OwnedVarInput {
170    /// Converts this owned variable input as a [`VarInput`].
171    pub fn borrow(&self) -> VarInput<'_> {
172        match self {
173            OwnedVarInput::Flat(v) => VarInput::Flat(v),
174            OwnedVarInput::SqlSet(v) => VarInput::SqlSet(v),
175        }
176    }
177}
178
179/// A `Var` represents a configuration parameter of an arbitrary type.
180pub trait Var: Debug {
181    /// Returns the name of the configuration parameter.
182    fn name(&self) -> &'static str;
183
184    /// Constructs a flattened string representation of the current value of the
185    /// configuration parameter.
186    ///
187    /// The resulting string is guaranteed to be parsable if provided to
188    /// `Value::parse` as a [`VarInput::Flat`].
189    fn value(&self) -> String;
190
191    /// Returns a short sentence describing the purpose of the configuration
192    /// parameter.
193    fn description(&self) -> &'static str;
194
195    /// Returns the name of the type of this variable.
196    fn type_name(&self) -> Cow<'static, str>;
197
198    /// Indicates wither the [`Var`] is visible as a function of the `user` and `system_vars`.
199    /// "Invisible" parameters return `VarErrors`.
200    ///
201    /// Variables marked as `internal` are only visible for the system user.
202    fn visible(&self, user: &User, system_vars: &SystemVars) -> Result<(), VarError>;
203
204    /// Reports whether the variable is only visible in unsafe mode.
205    fn is_unsafe(&self) -> bool {
206        self.name().starts_with("unsafe_")
207    }
208
209    /// Returns the [`ParameterScope`] at which this variable's value may be
210    /// overridden by the LaunchDarkly sync loop. Defaults to
211    /// [`ParameterScope::Environment`].
212    fn scope(&self) -> ParameterScope {
213        ParameterScope::Environment
214    }
215
216    /// Upcast `self` to a `dyn Var`, useful when working with multiple different implementors of
217    /// [`Var`].
218    fn as_var(&self) -> &dyn Var
219    where
220        Self: Sized,
221    {
222        self
223    }
224}
225
226/// A `SessionVar` is the session value for a configuration parameter. If unset,
227/// the server default is used instead.
228///
229/// Note: even though all of the different `*_value` fields are `Box<dyn Value>` they are enforced
230/// to be the same type because we use the `definition`s `parse(...)` method. This is guaranteed to
231/// return the same type as the compiled in default.
232#[derive(Debug)]
233pub struct SessionVar {
234    definition: VarDefinition,
235    /// System or Role default value.
236    default_value: Option<Box<dyn Value>>,
237    /// Value `LOCAL` to a transaction, will be unset at the completion of the transaction.
238    local_value: Option<Box<dyn Value>>,
239    /// Value set during a transaction, will be set if the transaction is committed.
240    staged_value: Option<Box<dyn Value>>,
241    /// Value that overrides the default.
242    session_value: Option<Box<dyn Value>>,
243}
244
245impl Clone for SessionVar {
246    fn clone(&self) -> Self {
247        SessionVar {
248            definition: self.definition.clone(),
249            default_value: self.default_value.as_ref().map(|v| v.box_clone()),
250            local_value: self.local_value.as_ref().map(|v| v.box_clone()),
251            staged_value: self.staged_value.as_ref().map(|v| v.box_clone()),
252            session_value: self.session_value.as_ref().map(|v| v.box_clone()),
253        }
254    }
255}
256
257impl SessionVar {
258    pub const fn new(var: VarDefinition) -> Self {
259        SessionVar {
260            definition: var,
261            default_value: None,
262            local_value: None,
263            staged_value: None,
264            session_value: None,
265        }
266    }
267
268    /// Checks if the provided [`VarInput`] is valid for the current session variable, returning
269    /// the formatted output if it's valid.
270    pub fn check(&self, input: VarInput) -> Result<String, VarError> {
271        let v = self.definition.parse(input)?;
272        self.validate_constraints(v.as_ref())?;
273
274        Ok(v.format())
275    }
276
277    /// Parse the input and update the stored value to match.
278    pub fn set(&mut self, input: VarInput, local: bool) -> Result<(), VarError> {
279        let v = self.definition.parse(input)?;
280
281        // Validate our parsed value.
282        self.validate_constraints(v.as_ref())?;
283
284        if local {
285            self.local_value = Some(v);
286        } else {
287            self.local_value = None;
288            self.staged_value = Some(v);
289        }
290        Ok(())
291    }
292
293    /// Sets the default value for the variable.
294    pub fn set_default(&mut self, input: VarInput) -> Result<(), VarError> {
295        let v = self.definition.parse(input)?;
296        self.validate_constraints(v.as_ref())?;
297        self.default_value = Some(v);
298        Ok(())
299    }
300
301    /// Reset the stored value to the default.
302    pub fn reset(&mut self, local: bool) {
303        let value = self
304            .default_value
305            .as_ref()
306            .map(|v| v.as_ref())
307            .unwrap_or_else(|| self.definition.value.value());
308        if local {
309            self.local_value = Some(value.box_clone());
310        } else {
311            self.local_value = None;
312            self.staged_value = Some(value.box_clone());
313        }
314    }
315
316    /// Resets the variable to its default, discarding any session, staged, or
317    /// local override. Unlike [`SessionVar::reset`], which stages the reset for
318    /// the current transaction, this takes effect immediately and does not
319    /// depend on a later [`SessionVar::end_transaction`] commit.
320    ///
321    /// `DISCARD ALL` requires this: it ends the transaction before resetting, so
322    /// there is no commit left to promote a staged value. `default_value` (the
323    /// system/role/startup default) is deliberately preserved.
324    pub fn reset_durable(&mut self) {
325        self.local_value = None;
326        self.staged_value = None;
327        self.session_value = None;
328    }
329
330    /// Returns a possibly new SessionVar if this needs to mutate at transaction end.
331    #[must_use]
332    pub fn end_transaction(&self, action: EndTransactionAction) -> Option<Self> {
333        if !self.is_mutating() {
334            return None;
335        }
336        let mut next: Self = self.clone();
337        next.local_value = None;
338        match action {
339            EndTransactionAction::Commit if next.staged_value.is_some() => {
340                next.session_value = next.staged_value.take()
341            }
342            _ => next.staged_value = None,
343        }
344        Some(next)
345    }
346
347    /// Whether this Var needs to mutate at the end of a transaction.
348    pub fn is_mutating(&self) -> bool {
349        self.local_value.is_some() || self.staged_value.is_some()
350    }
351
352    pub fn value_dyn(&self) -> &dyn Value {
353        self.local_value
354            .as_deref()
355            .or(self.staged_value.as_deref())
356            .or(self.session_value.as_deref())
357            .or(self.default_value.as_deref())
358            .unwrap_or_else(|| self.definition.value.value())
359    }
360
361    /// Returns the [`Value`] that is currently stored as the `session_value`.
362    ///
363    /// Note: This should __only__ be used for inspection, if you want to determine the current
364    /// value of this [`SessionVar`] you should use [`SessionVar::value`].
365    pub fn inspect_session_value(&self) -> Option<&dyn Value> {
366        self.session_value.as_deref()
367    }
368
369    fn validate_constraints(&self, val: &dyn Value) -> Result<(), VarError> {
370        if let Some(constraint) = &self.definition.constraint {
371            constraint.check_constraint(self, self.value_dyn(), val)
372        } else {
373            Ok(())
374        }
375    }
376}
377
378impl Var for SessionVar {
379    fn name(&self) -> &'static str {
380        self.definition.name.as_str()
381    }
382
383    fn value(&self) -> String {
384        self.value_dyn().format()
385    }
386
387    fn description(&self) -> &'static str {
388        self.definition.description
389    }
390
391    fn type_name(&self) -> Cow<'static, str> {
392        self.definition.type_name()
393    }
394
395    fn visible(
396        &self,
397        user: &User,
398        system_vars: &super::vars::SystemVars,
399    ) -> Result<(), super::vars::VarError> {
400        self.definition.visible(user, system_vars)
401    }
402}
403
404#[derive(Debug, Clone, PartialEq, Eq)]
405pub struct MzVersion {
406    /// Inputs to computed variables.
407    build_info: &'static BuildInfo,
408    /// Helm chart version
409    helm_chart_version: Option<String>,
410}
411
412impl MzVersion {
413    pub fn new(build_info: &'static BuildInfo, helm_chart_version: Option<String>) -> Self {
414        MzVersion {
415            build_info,
416            helm_chart_version,
417        }
418    }
419}
420
421/// Session variables.
422///
423/// See the [`crate::session::vars`] module documentation for more details on the
424/// Materialize configuration model.
425#[derive(Debug, Clone)]
426pub struct SessionVars {
427    /// The set of all session variables.
428    vars: OrdMap<&'static UncasedStr, SessionVar>,
429    /// Inputs to computed variables.
430    mz_version: MzVersion,
431    /// Information about the user associated with this Session.
432    user: User,
433}
434
435impl SessionVars {
436    /// Creates a new [`SessionVars`] without considering the System or Role defaults.
437    pub fn new_unchecked(
438        build_info: &'static BuildInfo,
439        user: User,
440        helm_chart_version: Option<String>,
441    ) -> SessionVars {
442        use definitions::*;
443
444        let vars = [
445            &FAILPOINTS,
446            &SERVER_VERSION,
447            &SERVER_VERSION_NUM,
448            &SQL_SAFE_UPDATES,
449            &REAL_TIME_RECENCY,
450            &EMIT_PLAN_INSIGHTS_NOTICE,
451            &EMIT_TIMESTAMP_NOTICE,
452            &EMIT_TRACE_ID_NOTICE,
453            &AUTO_ROUTE_CATALOG_QUERIES,
454            &ENABLE_SESSION_RBAC_CHECKS,
455            &RESTRICT_TO_USER_OBJECTS,
456            &ENABLE_SESSION_CARDINALITY_ESTIMATES,
457            &MAX_IDENTIFIER_LENGTH,
458            &STATEMENT_LOGGING_SAMPLE_RATE,
459            &EMIT_INTROSPECTION_QUERY_NOTICE,
460            &UNSAFE_NEW_TRANSACTION_WALL_TIME,
461            &WELCOME_MESSAGE,
462        ]
463        .into_iter()
464        .chain(SESSION_SYSTEM_VARS.iter().map(|(_name, var)| *var))
465        .map(|var| (var.name, SessionVar::new(var.clone())))
466        .collect();
467
468        SessionVars {
469            vars,
470            mz_version: MzVersion::new(build_info, helm_chart_version),
471            user,
472        }
473    }
474
475    fn expect_value<V: Value>(&self, var: &VarDefinition) -> &V {
476        let var = self
477            .vars
478            .get(var.name)
479            .expect("provided var should be in state");
480        let val = var.value_dyn();
481        val.as_any().downcast_ref::<V>().expect("success")
482    }
483
484    /// Returns an iterator over the configuration parameters and their current
485    /// values for this session.
486    ///
487    /// Note that this function does not check that the access variable should
488    /// be visible because of other settings or users. Before or after accessing
489    /// this method, you should call `Var::visible`.
490    pub fn iter(&self) -> impl Iterator<Item = &dyn Var> {
491        #[allow(clippy::as_conversions)]
492        self.vars
493            .values()
494            .map(|v| v.as_var())
495            .chain([&self.mz_version as &dyn Var, &self.user])
496    }
497
498    /// Returns an iterator over configuration parameters (and their current
499    /// values for this session) that are expected to be sent to the client when
500    /// a new connection is established or when their value changes.
501    pub fn notify_set(&self) -> impl Iterator<Item = &dyn Var> {
502        // WARNING: variables in this set are not checked for visibility, and
503        // are assumed to be visible for all sessions.
504        //
505        // This is fixible with some elbow grease, but at the moment it seems
506        // unlikely that we'll have a variable in the notify set that shouldn't
507        // be visible to all sessions.
508        [
509            &APPLICATION_NAME,
510            &CLIENT_ENCODING,
511            &DATE_STYLE,
512            &INTEGER_DATETIMES,
513            &SERVER_VERSION,
514            &STANDARD_CONFORMING_STRINGS,
515            &TIMEZONE,
516            &INTERVAL_STYLE,
517            // Including `cluster`, `cluster_replica`, `database`, and `search_path` in the notify
518            // set is a Materialize extension. Doing so allows users to more easily identify where
519            // their queries will be executing, which is important to know when you consider the
520            // size of a cluster, what indexes are present, etc.
521            &CLUSTER,
522            &CLUSTER_REPLICA,
523            &DEFAULT_CLUSTER_REPLICATION_FACTOR,
524            &DATABASE,
525            &SEARCH_PATH,
526        ]
527        .into_iter()
528        .map(|v| self.vars[v.name].as_var())
529        // Including `mz_version` in the notify set is a Materialize
530        // extension. Doing so allows applications to detect whether they
531        // are talking to Materialize or PostgreSQL without an additional
532        // network roundtrip. This is known to be safe because CockroachDB
533        // has an analogous extension [0].
534        // [0]: https://github.com/cockroachdb/cockroach/blob/369c4057a/pkg/sql/pgwire/conn.go#L1840
535        .chain(std::iter::once(self.mz_version.as_var()))
536    }
537
538    /// Durably resets all variables to their default value.
539    ///
540    /// Unlike a staged [`SessionVar::reset`], this takes effect immediately and
541    /// does not depend on a later transaction commit. Used by `DISCARD ALL`,
542    /// which ends the transaction before resetting, so there is no commit left
543    /// to promote a staged value. System/role/startup defaults are preserved.
544    pub fn reset_all(&mut self) {
545        let names: Vec<_> = self.vars.keys().copied().collect();
546        for name in names {
547            self.vars[name].reset_durable();
548        }
549    }
550
551    /// Returns a [`Var`] representing the configuration parameter with the
552    /// specified name.
553    ///
554    /// Configuration parameters are matched case insensitively. If no such
555    /// configuration parameter exists, `get` returns an error.
556    ///
557    /// Note that if `name` is known at compile time, you should instead use the
558    /// named accessor to access the variable with its true Rust type. For
559    /// example, `self.get("sql_safe_updates").value()` returns the string
560    /// `"true"` or `"false"`, while `self.sql_safe_updates()` returns a bool.
561    pub fn get(&self, system_vars: &SystemVars, name: &str) -> Result<&dyn Var, VarError> {
562        let name = compat_translate_name(name);
563
564        let name = UncasedStr::new(name);
565        if name == MZ_VERSION_NAME {
566            Ok(&self.mz_version)
567        } else if name == IS_SUPERUSER_NAME {
568            Ok(&self.user)
569        } else {
570            self.vars
571                .get(name)
572                .map(|v| {
573                    v.visible(&self.user, system_vars)?;
574                    Ok(v.as_var())
575                })
576                .transpose()?
577                .ok_or_else(|| VarError::UnknownParameter(name.to_string()))
578        }
579    }
580
581    /// Returns a [`SessionVar`] for inspection.
582    ///
583    /// Note: If you're trying to determine the value of the variable with `name` you should
584    /// instead use the named accessor, or [`SessionVars::get`].
585    pub fn inspect(&self, name: &str) -> Result<&SessionVar, VarError> {
586        let name = compat_translate_name(name);
587
588        self.vars
589            .get(UncasedStr::new(name))
590            .ok_or_else(|| VarError::UnknownParameter(name.to_string()))
591    }
592
593    /// Sets the configuration parameter named `name` to the value represented
594    /// by `value`.
595    ///
596    /// The new value may be either committed or rolled back by the next call to
597    /// [`SessionVars::end_transaction`]. If `local` is true, the new value is always
598    /// discarded by the next call to [`SessionVars::end_transaction`], even if the
599    /// transaction is marked to commit.
600    ///
601    /// Like with [`SessionVars::get`], configuration parameters are matched case
602    /// insensitively. If `value` is not valid, as determined by the underlying
603    /// configuration parameter, or if the named configuration parameter does
604    /// not exist, an error is returned.
605    pub fn set(
606        &mut self,
607        system_vars: &SystemVars,
608        name: &str,
609        input: VarInput,
610        local: bool,
611    ) -> Result<(), VarError> {
612        let (name, input) = compat_translate(name, input);
613
614        check_transaction_isolation_feature_flag(name, input, system_vars)?;
615
616        let name = UncasedStr::new(name);
617        self.check_read_only(name)?;
618
619        self.vars
620            .get_mut(name)
621            .map(|v| {
622                v.visible(&self.user, system_vars)?;
623                v.set(input, local)
624            })
625            .transpose()?
626            .ok_or_else(|| VarError::UnknownParameter(name.to_string()))
627    }
628
629    /// Sets the default value for the parameter named `name` to the value
630    /// represented by `value`.
631    pub fn set_default(&mut self, name: &str, input: VarInput) -> Result<(), VarError> {
632        let (name, input) = compat_translate(name, input);
633
634        let name = UncasedStr::new(name);
635
636        // Check if this variable is allowed to be set as a role default.
637        // Most read-only variables are blocked, but some (like restrict_to_user_objects)
638        // are specifically designed to be set via ALTER ROLE by superusers.
639        if !Self::allow_role_default(name) {
640            self.check_read_only(name)?;
641        }
642
643        self.vars
644            .get_mut(name)
645            // Note: visibility is checked when persisting a role default.
646            .map(|v| v.set_default(input))
647            .transpose()?
648            .ok_or_else(|| VarError::UnknownParameter(name.to_string()))
649    }
650
651    /// Returns true if the variable can be set as a role default even if it's
652    /// otherwise read-only from direct SET commands.
653    ///
654    /// SECURITY: Any variable listed here must also have a corresponding
655    /// superuser RBAC check in `generate_rbac_requirements` in `rbac.rs`
656    /// (see the `PlannedAlterRoleOption::Variable` match arm). Without that
657    /// check, any role could set the variable on themselves via ALTER ROLE.
658    fn allow_role_default(name: &UncasedStr) -> bool {
659        name == RESTRICT_TO_USER_OBJECTS.name
660    }
661
662    /// Sets the configuration parameter named `name` to its default value.
663    ///
664    /// The new value may be either committed or rolled back by the next call to
665    /// [`SessionVars::end_transaction`]. If `local` is true, the new value is
666    /// always discarded by the next call to [`SessionVars::end_transaction`],
667    /// even if the transaction is marked to commit.
668    ///
669    /// Like with [`SessionVars::get`], configuration parameters are matched
670    /// case insensitively. If the named configuration parameter does not exist,
671    /// an error is returned.
672    ///
673    /// If the variable does not exist or the user does not have the visibility
674    /// requires, this function returns an error.
675    pub fn reset(
676        &mut self,
677        system_vars: &SystemVars,
678        name: &str,
679        local: bool,
680    ) -> Result<(), VarError> {
681        let name = compat_translate_name(name);
682
683        let name = UncasedStr::new(name);
684        self.check_read_only(name)?;
685
686        self.vars
687            .get_mut(name)
688            .map(|v| {
689                v.visible(&self.user, system_vars)?;
690                v.reset(local);
691                Ok(())
692            })
693            .transpose()?
694            .ok_or_else(|| VarError::UnknownParameter(name.to_string()))
695    }
696
697    /// Returns an error if the variable corresponding to `name` is read only.
698    ///
699    /// Note: This is called by `set()` (for SQL SET commands) but NOT by
700    /// `set_default()` (for role defaults). This allows variables like
701    /// `restrict_to_user_objects` to be set via `ALTER ROLE ... SET` by
702    /// superusers while blocking direct `SET` commands from regular users.
703    fn check_read_only(&self, name: &UncasedStr) -> Result<(), VarError> {
704        if name == MZ_VERSION_NAME {
705            Err(VarError::ReadOnlyParameter(MZ_VERSION_NAME.as_str()))
706        } else if name == IS_SUPERUSER_NAME {
707            Err(VarError::ReadOnlyParameter(IS_SUPERUSER_NAME.as_str()))
708        } else if name == MAX_IDENTIFIER_LENGTH.name {
709            Err(VarError::ReadOnlyParameter(
710                MAX_IDENTIFIER_LENGTH.name.as_str(),
711            ))
712        } else if name == RESTRICT_TO_USER_OBJECTS.name {
713            // This variable can only be set via ALTER ROLE ... SET by superusers,
714            // not via direct SET commands. This prevents malicious queries from
715            // bypassing the restriction.
716            Err(VarError::ReadOnlyParameter(
717                RESTRICT_TO_USER_OBJECTS.name.as_str(),
718            ))
719        } else {
720            Ok(())
721        }
722    }
723
724    /// Commits or rolls back configuration parameter updates made via
725    /// [`SessionVars::set`] since the last call to `end_transaction`.
726    ///
727    /// Returns any session parameters that changed because the transaction ended.
728    #[mz_ore::instrument(level = "debug")]
729    pub fn end_transaction(
730        &mut self,
731        action: EndTransactionAction,
732    ) -> BTreeMap<&'static str, String> {
733        let mut changed = BTreeMap::new();
734        let mut updates = Vec::new();
735        for (name, var) in self.vars.iter() {
736            if !var.is_mutating() {
737                continue;
738            }
739            let before = var.value();
740            let next = var.end_transaction(action).expect("must mutate");
741            let after = next.value();
742            updates.push((*name, next));
743
744            // Report the new value of the parameter.
745            if before != after {
746                changed.insert(var.name(), after);
747            }
748        }
749        self.vars.extend(updates);
750        changed
751    }
752
753    /// Returns the value of the `application_name` configuration parameter.
754    pub fn application_name(&self) -> &str {
755        self.expect_value::<String>(&APPLICATION_NAME).as_str()
756    }
757
758    /// Returns the build info.
759    pub fn build_info(&self) -> &'static BuildInfo {
760        self.mz_version.build_info
761    }
762
763    /// Returns the value of the `client_encoding` configuration parameter.
764    pub fn client_encoding(&self) -> &ClientEncoding {
765        self.expect_value(&CLIENT_ENCODING)
766    }
767
768    /// Returns the value of the `client_min_messages` configuration parameter.
769    pub fn client_min_messages(&self) -> &ClientSeverity {
770        self.expect_value(&CLIENT_MIN_MESSAGES)
771    }
772
773    /// Returns the value of the `cluster` configuration parameter.
774    pub fn cluster(&self) -> &str {
775        self.expect_value::<String>(&CLUSTER).as_str()
776    }
777
778    /// Returns the value of the `cluster_replica` configuration parameter.
779    pub fn cluster_replica(&self) -> Option<&str> {
780        self.expect_value::<Option<String>>(&CLUSTER_REPLICA)
781            .as_deref()
782    }
783
784    /// Returns the value of the `current_object_missing_warnings` configuration
785    /// parameter.
786    pub fn current_object_missing_warnings(&self) -> bool {
787        *self.expect_value::<bool>(&CURRENT_OBJECT_MISSING_WARNINGS)
788    }
789
790    /// Returns the value of the `DateStyle` configuration parameter.
791    pub fn date_style(&self) -> &[&str] {
792        &self.expect_value::<DateStyle>(&DATE_STYLE).0
793    }
794
795    /// Returns the value of the `database` configuration parameter.
796    pub fn database(&self) -> &str {
797        self.expect_value::<String>(&DATABASE).as_str()
798    }
799
800    /// Returns the value of the `extra_float_digits` configuration parameter.
801    pub fn extra_float_digits(&self) -> i32 {
802        *self.expect_value(&EXTRA_FLOAT_DIGITS)
803    }
804
805    /// Returns the settings that govern how this session encodes values as
806    /// text.
807    pub fn text_encode_settings(&self) -> TextEncodeSettings {
808        TextEncodeSettings {
809            extra_float_digits: self.extra_float_digits(),
810        }
811    }
812
813    /// Returns the value of the `integer_datetimes` configuration parameter.
814    pub fn integer_datetimes(&self) -> bool {
815        *self.expect_value(&INTEGER_DATETIMES)
816    }
817
818    /// Returns the value of the `intervalstyle` configuration parameter.
819    pub fn intervalstyle(&self) -> &IntervalStyle {
820        self.expect_value(&INTERVAL_STYLE)
821    }
822
823    /// Returns the value of the `mz_version` configuration parameter.
824    pub fn mz_version(&self) -> String {
825        self.mz_version.value()
826    }
827
828    /// Returns the value of the `search_path` configuration parameter.
829    pub fn search_path(&self) -> &[Ident] {
830        self.expect_value::<Vec<Ident>>(&SEARCH_PATH).as_slice()
831    }
832
833    /// Returns the value of the `server_version` configuration parameter.
834    pub fn server_version(&self) -> &str {
835        self.expect_value::<String>(&SERVER_VERSION).as_str()
836    }
837
838    /// Returns the value of the `server_version_num` configuration parameter.
839    pub fn server_version_num(&self) -> i32 {
840        *self.expect_value(&SERVER_VERSION_NUM)
841    }
842
843    /// Returns the value of the `sql_safe_updates` configuration parameter.
844    pub fn sql_safe_updates(&self) -> bool {
845        *self.expect_value(&SQL_SAFE_UPDATES)
846    }
847
848    /// Returns the value of the `standard_conforming_strings` configuration
849    /// parameter.
850    pub fn standard_conforming_strings(&self) -> bool {
851        *self.expect_value(&STANDARD_CONFORMING_STRINGS)
852    }
853
854    /// Returns the value of the `statement_timeout` configuration parameter.
855    pub fn statement_timeout(&self) -> &Duration {
856        self.expect_value(&STATEMENT_TIMEOUT)
857    }
858
859    /// Returns the value of the `idle_in_transaction_session_timeout` configuration parameter.
860    pub fn idle_in_transaction_session_timeout(&self) -> &Duration {
861        self.expect_value(&IDLE_IN_TRANSACTION_SESSION_TIMEOUT)
862    }
863
864    /// Returns the value of the `timezone` configuration parameter.
865    pub fn timezone(&self) -> &TimeZone {
866        self.expect_value(&TIMEZONE)
867    }
868
869    /// Returns the value of the `transaction_isolation` configuration
870    /// parameter.
871    pub fn transaction_isolation(&self) -> &IsolationLevel {
872        self.expect_value(&TRANSACTION_ISOLATION)
873    }
874
875    /// Returns the value of `real_time_recency` configuration parameter.
876    pub fn real_time_recency(&self) -> bool {
877        *self.expect_value(&REAL_TIME_RECENCY)
878    }
879
880    /// Returns the value of the `real_time_recency_timeout` configuration parameter.
881    pub fn real_time_recency_timeout(&self) -> &Duration {
882        self.expect_value(&REAL_TIME_RECENCY_TIMEOUT)
883    }
884
885    /// Returns the value of `emit_plan_insights_notice` configuration parameter.
886    pub fn emit_plan_insights_notice(&self) -> bool {
887        *self.expect_value(&EMIT_PLAN_INSIGHTS_NOTICE)
888    }
889
890    /// Returns the value of `emit_timestamp_notice` configuration parameter.
891    pub fn emit_timestamp_notice(&self) -> bool {
892        *self.expect_value(&EMIT_TIMESTAMP_NOTICE)
893    }
894
895    /// Returns the value of `emit_trace_id_notice` configuration parameter.
896    pub fn emit_trace_id_notice(&self) -> bool {
897        *self.expect_value(&EMIT_TRACE_ID_NOTICE)
898    }
899
900    /// Returns the value of `auto_route_catalog_queries` configuration parameter.
901    pub fn auto_route_catalog_queries(&self) -> bool {
902        *self.expect_value(&AUTO_ROUTE_CATALOG_QUERIES)
903    }
904
905    /// Returns the value of `enable_session_rbac_checks` configuration parameter.
906    pub fn enable_session_rbac_checks(&self) -> bool {
907        *self.expect_value(&ENABLE_SESSION_RBAC_CHECKS)
908    }
909
910    /// Returns the value of `restrict_to_user_objects` configuration parameter.
911    pub fn restrict_to_user_objects(&self) -> bool {
912        *self.expect_value(&RESTRICT_TO_USER_OBJECTS)
913    }
914
915    /// Returns the value of `enable_session_cardinality_estimates` configuration parameter.
916    pub fn enable_session_cardinality_estimates(&self) -> bool {
917        *self.expect_value(&ENABLE_SESSION_CARDINALITY_ESTIMATES)
918    }
919
920    /// Returns the value of `is_superuser` configuration parameter.
921    pub fn is_superuser(&self) -> bool {
922        self.user.is_superuser()
923    }
924
925    /// Returns the user associated with this `SessionVars` instance.
926    pub fn user(&self) -> &User {
927        &self.user
928    }
929
930    /// Returns the value of the `max_query_result_size` configuration parameter.
931    pub fn max_query_result_size(&self) -> u64 {
932        self.expect_value::<ByteSize>(&MAX_QUERY_RESULT_SIZE)
933            .as_bytes()
934    }
935
936    /// Sets the internal metadata associated with the user.
937    pub fn set_internal_user_metadata(&mut self, metadata: InternalUserMetadata) {
938        self.user.internal_metadata = Some(metadata);
939    }
940
941    /// Sets the external metadata associated with the user.
942    pub fn set_external_user_metadata(&mut self, metadata: ExternalUserMetadata) {
943        self.user.external_metadata = Some(metadata);
944    }
945
946    pub fn set_cluster(&mut self, cluster: String) {
947        let var = self
948            .vars
949            .get_mut(UncasedStr::new(CLUSTER.name()))
950            .expect("cluster variable must exist");
951        var.set(VarInput::Flat(&cluster), false)
952            .expect("setting cluster must succeed");
953    }
954
955    pub fn set_local_transaction_isolation(&mut self, transaction_isolation: IsolationLevel) {
956        let var = self
957            .vars
958            .get_mut(UncasedStr::new(TRANSACTION_ISOLATION.name()))
959            .expect("transaction_isolation variable must exist");
960        var.set(VarInput::Flat(&transaction_isolation.to_string()), true)
961            .expect("setting transaction isolation must succeed");
962    }
963
964    pub fn get_statement_logging_sample_rate(&self) -> Numeric {
965        *self.expect_value(&STATEMENT_LOGGING_SAMPLE_RATE)
966    }
967
968    /// Returns the value of the `emit_introspection_query_notice` configuration parameter.
969    pub fn emit_introspection_query_notice(&self) -> bool {
970        *self.expect_value(&EMIT_INTROSPECTION_QUERY_NOTICE)
971    }
972
973    pub fn unsafe_new_transaction_wall_time(&self) -> Option<CheckedTimestamp<DateTime<Utc>>> {
974        *self.expect_value(&UNSAFE_NEW_TRANSACTION_WALL_TIME)
975    }
976
977    /// Returns the value of the `welcome_message` configuration parameter.
978    pub fn welcome_message(&self) -> bool {
979        *self.expect_value(&WELCOME_MESSAGE)
980    }
981}
982
983// TODO(database-issues#8069) remove together with `compat_translate`
984pub const OLD_CATALOG_SERVER_CLUSTER: &str = "mz_introspection";
985pub const OLD_AUTO_ROUTE_CATALOG_QUERIES: &str = "auto_route_introspection_queries";
986
987/// If the given variable name and/or input is deprecated, return a corresponding updated value,
988/// otherwise return the original.
989///
990/// This method was introduced to gracefully handle the rename of the `mz_introspection` cluster to
991/// `mz_cluster_server`. The plan is to remove it once all users have migrated to the new name. The
992/// debug logs will be helpful for checking this in production.
993// TODO(database-issues#8069) remove this after sufficient time has passed
994fn compat_translate<'a, 'b>(name: &'a str, input: VarInput<'b>) -> (&'a str, VarInput<'b>) {
995    if name == CLUSTER.name() {
996        if let Ok(value) = CLUSTER.parse(input) {
997            if value.format() == OLD_CATALOG_SERVER_CLUSTER {
998                tracing::debug!(
999                    github_27285 = true,
1000                    "encountered deprecated `cluster` variable value: {}",
1001                    OLD_CATALOG_SERVER_CLUSTER,
1002                );
1003                return (name, VarInput::Flat("mz_catalog_server"));
1004            }
1005        }
1006    }
1007
1008    if name == OLD_AUTO_ROUTE_CATALOG_QUERIES {
1009        tracing::debug!(
1010            github_27285 = true,
1011            "encountered deprecated `{}` variable name",
1012            OLD_AUTO_ROUTE_CATALOG_QUERIES,
1013        );
1014        return (AUTO_ROUTE_CATALOG_QUERIES.name(), input);
1015    }
1016
1017    (name, input)
1018}
1019
1020fn compat_translate_name(name: &str) -> &str {
1021    let (name, _) = compat_translate(name, VarInput::Flat(""));
1022    name
1023}
1024
1025/// Enforces feature-flag gating for `transaction_isolation` levels that sit
1026/// behind a flag (`strong session serializable`).
1027///
1028/// Returns `Ok(())` for any other variable, and for an unparseable value
1029/// (parse errors surface on the actual set). This is shared by every path that
1030/// assigns `transaction_isolation` — `SET`, `SET TRANSACTION`,
1031/// `ALTER ROLE ... SET`, and connection options — so that the gate cannot be
1032/// bypassed by choosing a different syntax or letter case.
1033pub fn check_transaction_isolation_feature_flag(
1034    name: &str,
1035    input: VarInput,
1036    system_vars: &SystemVars,
1037) -> Result<(), VarError> {
1038    if UncasedStr::new(name) != UncasedStr::new(TRANSACTION_ISOLATION_VAR_NAME) {
1039        return Ok(());
1040    }
1041    // Ignore parse failures here; the actual set surfaces them.
1042    let Ok(level) = IsolationLevel::parse(input) else {
1043        return Ok(());
1044    };
1045    match level {
1046        IsolationLevel::StrongSessionSerializable => ENABLE_SESSION_TIMELINES.require(system_vars),
1047        _ => Ok(()),
1048    }
1049}
1050
1051/// A `SystemVar` is persisted on disk value for a configuration parameter. If unset,
1052/// the server default is used instead.
1053#[derive(Debug)]
1054pub struct SystemVar {
1055    definition: VarDefinition,
1056    /// Value currently persisted to disk.
1057    persisted_value: Option<Box<dyn Value>>,
1058    /// Current default, not persisted to disk.
1059    dynamic_default: Option<Box<dyn Value>>,
1060}
1061
1062impl Clone for SystemVar {
1063    fn clone(&self) -> Self {
1064        SystemVar {
1065            definition: self.definition.clone(),
1066            persisted_value: self.persisted_value.as_ref().map(|v| v.box_clone()),
1067            dynamic_default: self.dynamic_default.as_ref().map(|v| v.box_clone()),
1068        }
1069    }
1070}
1071
1072impl SystemVar {
1073    pub fn new(definition: VarDefinition) -> Self {
1074        SystemVar {
1075            definition,
1076            persisted_value: None,
1077            dynamic_default: None,
1078        }
1079    }
1080
1081    fn is_default(&self, input: VarInput) -> Result<bool, VarError> {
1082        let v = self.definition.parse(input)?;
1083        Ok(self.definition.default_value() == v.as_ref())
1084    }
1085
1086    pub fn value_dyn(&self) -> &dyn Value {
1087        self.persisted_value
1088            .as_deref()
1089            .or(self.dynamic_default.as_deref())
1090            .unwrap_or_else(|| self.definition.default_value())
1091    }
1092
1093    pub fn value<V: 'static>(&self) -> &V {
1094        let val = self.value_dyn();
1095        val.as_any().downcast_ref::<V>().expect("success")
1096    }
1097
1098    fn parse(&self, input: VarInput) -> Result<Box<dyn Value>, VarError> {
1099        let v = self.definition.parse(input)?;
1100        // Validate our parsed value.
1101        self.validate_constraints(v.as_ref())?;
1102        Ok(v)
1103    }
1104
1105    fn set(&mut self, input: VarInput) -> Result<bool, VarError> {
1106        let v = self.parse(input)?;
1107
1108        if self.persisted_value.as_ref() != Some(&v) {
1109            self.persisted_value = Some(v);
1110            Ok(true)
1111        } else {
1112            Ok(false)
1113        }
1114    }
1115
1116    fn reset(&mut self) -> bool {
1117        if self.persisted_value.is_some() {
1118            self.persisted_value = None;
1119            true
1120        } else {
1121            false
1122        }
1123    }
1124
1125    fn set_default(&mut self, input: VarInput) -> Result<(), VarError> {
1126        let v = self.parse(input)?;
1127        self.dynamic_default = Some(v);
1128        Ok(())
1129    }
1130
1131    fn validate_constraints(&self, val: &dyn Value) -> Result<(), VarError> {
1132        if let Some(constraint) = &self.definition.constraint {
1133            constraint.check_constraint(self, self.value_dyn(), val)
1134        } else {
1135            Ok(())
1136        }
1137    }
1138}
1139
1140impl Var for SystemVar {
1141    fn name(&self) -> &'static str {
1142        self.definition.name.as_str()
1143    }
1144
1145    fn value(&self) -> String {
1146        self.value_dyn().format()
1147    }
1148
1149    fn description(&self) -> &'static str {
1150        self.definition.description
1151    }
1152
1153    fn type_name(&self) -> Cow<'static, str> {
1154        self.definition.type_name()
1155    }
1156
1157    fn scope(&self) -> ParameterScope {
1158        self.definition.scope()
1159    }
1160
1161    fn visible(&self, user: &User, system_vars: &SystemVars) -> Result<(), VarError> {
1162        self.definition.visible(user, system_vars)
1163    }
1164}
1165
1166#[derive(Debug, Error)]
1167pub enum NetworkPolicyError {
1168    #[error("Access denied for address {0}")]
1169    AddressDenied(IpAddr),
1170}
1171
1172/// On disk variables.
1173///
1174/// See the [`crate::session::vars`] module documentation for more details on the
1175/// Materialize configuration model.
1176#[derive(Derivative, Clone)]
1177#[derivative(Debug)]
1178pub struct SystemVars {
1179    /// Allows "unsafe" parameters to be set.
1180    allow_unsafe: bool,
1181    /// Set of all [`SystemVar`]s.
1182    vars: BTreeMap<&'static UncasedStr, SystemVar>,
1183    /// External components interested in when a [`SystemVar`] gets updated.
1184    #[derivative(Debug = "ignore")]
1185    callbacks: BTreeMap<String, Vec<Arc<dyn Fn(&SystemVars) + Send + Sync>>>,
1186
1187    /// NB: This is intentionally disconnected from the one that is plumbed around to persist and
1188    /// the controllers. This is so we can explicitly control and reason about when changes to config
1189    /// values are propagated to the rest of the system.
1190    dyncfgs: ConfigSet,
1191}
1192
1193impl Default for SystemVars {
1194    fn default() -> Self {
1195        Self::new()
1196    }
1197}
1198
1199impl SystemVars {
1200    pub fn new() -> Self {
1201        let system_vars = vec![
1202            &MAX_KAFKA_CONNECTIONS,
1203            &MAX_POSTGRES_CONNECTIONS,
1204            &MAX_MYSQL_CONNECTIONS,
1205            &MAX_SQL_SERVER_CONNECTIONS,
1206            &MAX_AWS_PRIVATELINK_CONNECTIONS,
1207            &MAX_TABLES,
1208            &MAX_SOURCES,
1209            &MAX_SINKS,
1210            &MAX_MATERIALIZED_VIEWS,
1211            &MAX_CLUSTERS,
1212            &MAX_REPLICAS_PER_CLUSTER,
1213            &MAX_CREDIT_CONSUMPTION_RATE,
1214            &MAX_DATABASES,
1215            &MAX_SCHEMAS_PER_DATABASE,
1216            &MAX_OBJECTS_PER_SCHEMA,
1217            &MAX_SECRETS,
1218            &MAX_ROLES,
1219            &MAX_NETWORK_POLICIES,
1220            &MAX_RULES_PER_NETWORK_POLICY,
1221            &MAX_RESULT_SIZE,
1222            &MAX_COPY_FROM_ROW_SIZE,
1223            &ALLOWED_CLUSTER_REPLICA_SIZES,
1224            &MAX_CONCURRENT_OCC_WRITES,
1225            &MAX_OCC_RETRIES,
1226            &upsert_rocksdb::UPSERT_ROCKSDB_COMPACTION_STYLE,
1227            &upsert_rocksdb::UPSERT_ROCKSDB_OPTIMIZE_COMPACTION_MEMTABLE_BUDGET,
1228            &upsert_rocksdb::UPSERT_ROCKSDB_LEVEL_COMPACTION_DYNAMIC_LEVEL_BYTES,
1229            &upsert_rocksdb::UPSERT_ROCKSDB_UNIVERSAL_COMPACTION_RATIO,
1230            &upsert_rocksdb::UPSERT_ROCKSDB_PARALLELISM,
1231            &upsert_rocksdb::UPSERT_ROCKSDB_COMPRESSION_TYPE,
1232            &upsert_rocksdb::UPSERT_ROCKSDB_BOTTOMMOST_COMPRESSION_TYPE,
1233            &upsert_rocksdb::UPSERT_ROCKSDB_BATCH_SIZE,
1234            &upsert_rocksdb::UPSERT_ROCKSDB_RETRY_DURATION,
1235            &upsert_rocksdb::UPSERT_ROCKSDB_STATS_LOG_INTERVAL_SECONDS,
1236            &upsert_rocksdb::UPSERT_ROCKSDB_STATS_PERSIST_INTERVAL_SECONDS,
1237            &upsert_rocksdb::UPSERT_ROCKSDB_POINT_LOOKUP_BLOCK_CACHE_SIZE_MB,
1238            &upsert_rocksdb::UPSERT_ROCKSDB_SHRINK_ALLOCATED_BUFFERS_BY_RATIO,
1239            &upsert_rocksdb::UPSERT_ROCKSDB_WRITE_BUFFER_MANAGER_CLUSTER_MEMORY_FRACTION,
1240            &upsert_rocksdb::UPSERT_ROCKSDB_WRITE_BUFFER_MANAGER_MEMORY_BYTES,
1241            &upsert_rocksdb::UPSERT_ROCKSDB_WRITE_BUFFER_MANAGER_ALLOW_STALL,
1242            &STORAGE_DATAFLOW_MAX_INFLIGHT_BYTES,
1243            &STORAGE_DATAFLOW_MAX_INFLIGHT_BYTES_TO_CLUSTER_SIZE_FRACTION,
1244            &STORAGE_DATAFLOW_MAX_INFLIGHT_BYTES_DISK_ONLY,
1245            &STORAGE_STATISTICS_INTERVAL,
1246            &STORAGE_STATISTICS_COLLECTION_INTERVAL,
1247            &STORAGE_SHRINK_UPSERT_UNUSED_BUFFERS_BY_RATIO,
1248            &STORAGE_RECORD_SOURCE_SINK_NAMESPACED_ERRORS,
1249            &PERSIST_FAST_PATH_LIMIT,
1250            &METRICS_RETENTION,
1251            &UNSAFE_MOCK_AUDIT_EVENT_TIMESTAMP,
1252            &ENABLE_RBAC_CHECKS,
1253            &PG_SOURCE_CONNECT_TIMEOUT,
1254            &PG_SOURCE_TCP_KEEPALIVES_IDLE,
1255            &PG_SOURCE_TCP_KEEPALIVES_INTERVAL,
1256            &PG_SOURCE_TCP_KEEPALIVES_RETRIES,
1257            &PG_SOURCE_TCP_USER_TIMEOUT,
1258            &PG_SOURCE_TCP_CONFIGURE_SERVER,
1259            &PG_SOURCE_SNAPSHOT_STATEMENT_TIMEOUT,
1260            &PG_SOURCE_WAL_SENDER_TIMEOUT,
1261            &PG_SOURCE_SNAPSHOT_COLLECT_STRICT_COUNT,
1262            &MYSQL_SOURCE_TCP_KEEPALIVE,
1263            &MYSQL_SOURCE_SNAPSHOT_MAX_EXECUTION_TIME,
1264            &MYSQL_SOURCE_SNAPSHOT_LOCK_WAIT_TIMEOUT,
1265            &MYSQL_SOURCE_SNAPSHOT_WAIT_TIMEOUT,
1266            &MYSQL_SOURCE_CONNECT_TIMEOUT,
1267            &SSH_CHECK_INTERVAL,
1268            &SSH_CONNECT_TIMEOUT,
1269            &SSH_KEEPALIVES_IDLE,
1270            &KAFKA_SOCKET_KEEPALIVE,
1271            &KAFKA_SOCKET_TIMEOUT,
1272            &KAFKA_TRANSACTION_TIMEOUT,
1273            &KAFKA_SOCKET_CONNECTION_SETUP_TIMEOUT,
1274            &KAFKA_FETCH_METADATA_TIMEOUT,
1275            &KAFKA_PROGRESS_RECORD_FETCH_TIMEOUT,
1276            &ENABLE_LAUNCHDARKLY,
1277            &MAX_CONNECTIONS,
1278            &NETWORK_POLICY,
1279            &SUPERUSER_RESERVED_CONNECTIONS,
1280            &KEEP_N_SOURCE_STATUS_HISTORY_ENTRIES,
1281            &KEEP_N_SINK_STATUS_HISTORY_ENTRIES,
1282            &KEEP_N_PRIVATELINK_STATUS_HISTORY_ENTRIES,
1283            &REPLICA_STATUS_HISTORY_RETENTION_WINDOW,
1284            &ENABLE_STORAGE_SHARD_FINALIZATION,
1285            &ENABLE_DEFAULT_CONNECTION_VALIDATION,
1286            &DEFAULT_TIMESTAMP_INTERVAL,
1287            &MIN_TIMESTAMP_INTERVAL,
1288            &MAX_TIMESTAMP_INTERVAL,
1289            &LOGGING_FILTER,
1290            &OPENTELEMETRY_FILTER,
1291            &LOGGING_FILTER_DEFAULTS,
1292            &OPENTELEMETRY_FILTER_DEFAULTS,
1293            &SENTRY_FILTERS,
1294            &WEBHOOKS_SECRETS_CACHING_TTL_SECS,
1295            &COORD_SLOW_MESSAGE_WARN_THRESHOLD,
1296            &grpc_client::CONNECT_TIMEOUT,
1297            &grpc_client::HTTP2_KEEP_ALIVE_INTERVAL,
1298            &grpc_client::HTTP2_KEEP_ALIVE_TIMEOUT,
1299            &cluster_scheduling::CLUSTER_MULTI_PROCESS_REPLICA_AZ_AFFINITY_WEIGHT,
1300            &cluster_scheduling::CLUSTER_SOFTEN_REPLICATION_ANTI_AFFINITY,
1301            &cluster_scheduling::CLUSTER_SOFTEN_REPLICATION_ANTI_AFFINITY_WEIGHT,
1302            &cluster_scheduling::CLUSTER_ENABLE_TOPOLOGY_SPREAD,
1303            &cluster_scheduling::CLUSTER_TOPOLOGY_SPREAD_IGNORE_NON_SINGULAR_SCALE,
1304            &cluster_scheduling::CLUSTER_TOPOLOGY_SPREAD_MAX_SKEW,
1305            &cluster_scheduling::CLUSTER_TOPOLOGY_SPREAD_MIN_DOMAINS,
1306            &cluster_scheduling::CLUSTER_TOPOLOGY_SPREAD_SOFT,
1307            &cluster_scheduling::CLUSTER_SOFTEN_AZ_AFFINITY,
1308            &cluster_scheduling::CLUSTER_SOFTEN_AZ_AFFINITY_WEIGHT,
1309            &cluster_scheduling::CLUSTER_ALTER_CHECK_READY_INTERVAL,
1310            &cluster_scheduling::CLUSTER_SECURITY_CONTEXT_ENABLED,
1311            &cluster_scheduling::CLUSTER_REFRESH_MV_COMPACTION_ESTIMATE,
1312            &grpc_client::HTTP2_KEEP_ALIVE_TIMEOUT,
1313            &STATEMENT_LOGGING_MAX_SAMPLE_RATE,
1314            &STATEMENT_LOGGING_DEFAULT_SAMPLE_RATE,
1315            &STATEMENT_LOGGING_TARGET_DATA_RATE,
1316            &STATEMENT_LOGGING_MAX_DATA_CREDIT,
1317            &ENABLE_INTERNAL_STATEMENT_LOGGING,
1318            &ENABLE_STATEMENT_ARRIVAL_LOGGING,
1319            &ENABLE_EXTENDED_PROTOCOL_IMPLICIT_TRANSACTION,
1320            &OPTIMIZER_STATS_TIMEOUT,
1321            &OPTIMIZER_ONESHOT_STATS_TIMEOUT,
1322            &PRIVATELINK_STATUS_UPDATE_QUOTA_PER_MINUTE,
1323            &WEBHOOK_CONCURRENT_REQUEST_LIMIT,
1324            &PG_TIMESTAMP_ORACLE_CONNECTION_POOL_MAX_SIZE,
1325            &PG_TIMESTAMP_ORACLE_CONNECTION_POOL_MAX_WAIT,
1326            &PG_TIMESTAMP_ORACLE_CONNECTION_POOL_TTL,
1327            &PG_TIMESTAMP_ORACLE_CONNECTION_POOL_TTL_STAGGER,
1328            &USER_STORAGE_MANAGED_COLLECTIONS_BATCH_DURATION,
1329            &FORCE_SOURCE_TABLE_SYNTAX,
1330            &OPTIMIZER_E2E_LATENCY_WARNING_THRESHOLD,
1331            &SCRAM_ITERATIONS,
1332        ];
1333
1334        let dyncfgs = mz_dyncfgs::all_dyncfgs();
1335        let dyncfg_vars: Vec<_> = dyncfgs
1336            .entries()
1337            .map(|cfg| {
1338                let var = match cfg.default() {
1339                    ConfigVal::Bool(default) => {
1340                        VarDefinition::new_runtime(cfg.name(), *default, cfg.desc(), false)
1341                    }
1342                    ConfigVal::U32(default) => {
1343                        VarDefinition::new_runtime(cfg.name(), *default, cfg.desc(), false)
1344                    }
1345                    ConfigVal::Usize(default) => {
1346                        VarDefinition::new_runtime(cfg.name(), *default, cfg.desc(), false)
1347                    }
1348                    ConfigVal::OptUsize(default) => {
1349                        VarDefinition::new_runtime(cfg.name(), *default, cfg.desc(), false)
1350                    }
1351                    ConfigVal::F64(default) => {
1352                        VarDefinition::new_runtime(cfg.name(), *default, cfg.desc(), false)
1353                    }
1354                    ConfigVal::String(default) => {
1355                        VarDefinition::new_runtime(cfg.name(), default.clone(), cfg.desc(), false)
1356                    }
1357                    ConfigVal::OptString(default) => {
1358                        VarDefinition::new_runtime(cfg.name(), default.clone(), cfg.desc(), false)
1359                    }
1360                    ConfigVal::Duration(default) => {
1361                        VarDefinition::new_runtime(cfg.name(), default.clone(), cfg.desc(), false)
1362                    }
1363                    ConfigVal::Json(default) => {
1364                        VarDefinition::new_runtime(cfg.name(), default.clone(), cfg.desc(), false)
1365                    }
1366                };
1367                // Carry the dyncfg's declared scope through to the system var,
1368                // so scoped resolution and introspection see it.
1369                var.scoped(cfg.scope())
1370            })
1371            .collect();
1372
1373        let vars: BTreeMap<_, _> = system_vars
1374            .into_iter()
1375            // Include all of our feature flags.
1376            .chain(definitions::FEATURE_FLAGS.iter().copied())
1377            // Include the subset of Session variables we allow system defaults for.
1378            .chain(SESSION_SYSTEM_VARS.values().copied())
1379            .cloned()
1380            // Include Persist configs.
1381            .chain(dyncfg_vars)
1382            .map(|var| (var.name, SystemVar::new(var)))
1383            .collect();
1384
1385        let vars = SystemVars {
1386            vars,
1387            callbacks: BTreeMap::new(),
1388            allow_unsafe: false,
1389            dyncfgs,
1390        };
1391
1392        vars
1393    }
1394
1395    pub fn dyncfgs(&self) -> &ConfigSet {
1396        &self.dyncfgs
1397    }
1398
1399    pub fn set_unsafe(mut self, allow_unsafe: bool) -> Self {
1400        self.allow_unsafe = allow_unsafe;
1401        self
1402    }
1403
1404    pub fn allow_unsafe(&self) -> bool {
1405        self.allow_unsafe
1406    }
1407
1408    fn expect_value<V: 'static>(&self, var: &VarDefinition) -> &V {
1409        let val = self
1410            .vars
1411            .get(var.name)
1412            .expect("provided var should be in state");
1413
1414        val.value_dyn()
1415            .as_any()
1416            .downcast_ref::<V>()
1417            .expect("provided var type should matched stored var")
1418    }
1419
1420    fn expect_config_value<V: ConfigType + 'static>(&self, name: &UncasedStr) -> &V {
1421        let val = self
1422            .vars
1423            .get(name)
1424            .unwrap_or_else(|| panic!("provided var {name} should be in state"));
1425
1426        val.value_dyn()
1427            .as_any()
1428            .downcast_ref()
1429            .expect("provided var type should matched stored var")
1430    }
1431
1432    /// Returns an iterator over the configuration parameters and their current
1433    /// values on disk.
1434    pub fn iter(&self) -> impl Iterator<Item = &dyn Var> {
1435        self.vars
1436            .values()
1437            .map(|v| v.as_var())
1438            .filter(|v| !SESSION_SYSTEM_VARS.contains_key(UncasedStr::new(v.name())))
1439    }
1440
1441    /// Returns an iterator over the configuration parameters and their current
1442    /// values on disk. Compared to [`SystemVars::iter`], this should omit vars
1443    /// that shouldn't be synced by SystemParameterFrontend.
1444    pub fn iter_synced(&self) -> impl Iterator<Item = &dyn Var> {
1445        self.iter().filter(|v| v.name() != ENABLE_LAUNCHDARKLY.name)
1446    }
1447
1448    /// Returns an iterator over the configuration parameters that can be overriden per-Session.
1449    pub fn iter_session(&self) -> impl Iterator<Item = &dyn Var> {
1450        self.vars
1451            .values()
1452            .map(|v| v.as_var())
1453            .filter(|v| SESSION_SYSTEM_VARS.contains_key(UncasedStr::new(v.name())))
1454    }
1455
1456    /// Returns whether or not this parameter can be modified by a superuser.
1457    pub fn user_modifiable(&self, name: &str) -> bool {
1458        SESSION_SYSTEM_VARS.contains_key(UncasedStr::new(name))
1459            || name == ENABLE_RBAC_CHECKS.name()
1460            || name == NETWORK_POLICY.name()
1461    }
1462
1463    /// Returns a [`Var`] representing the configuration parameter with the
1464    /// specified name.
1465    ///
1466    /// Configuration parameters are matched case insensitively. If no such
1467    /// configuration parameter exists, `get` returns an error.
1468    ///
1469    /// Note that:
1470    /// - If `name` is known at compile time, you should instead use the named
1471    /// accessor to access the variable with its true Rust type. For example,
1472    /// `self.get("max_tables").value()` returns the string `"25"` or the
1473    /// current value, while `self.max_tables()` returns an i32.
1474    ///
1475    /// - This function does not check that the access variable should be
1476    /// visible because of other settings or users. Before or after accessing
1477    /// this method, you should call `Var::visible`.
1478    ///
1479    /// # Errors
1480    ///
1481    /// The call will return an error:
1482    /// 1. If `name` does not refer to a valid [`SystemVars`] field.
1483    pub fn get(&self, name: &str) -> Result<&dyn Var, VarError> {
1484        self.vars
1485            .get(UncasedStr::new(name))
1486            .map(|v| v.as_var())
1487            .ok_or_else(|| VarError::UnknownParameter(name.into()))
1488    }
1489
1490    /// Check if the given `values` is the default value for the [`Var`]
1491    /// identified by `name`.
1492    ///
1493    /// Note that this function does not check that the access variable should
1494    /// be visible because of other settings or users. Before or after accessing
1495    /// this method, you should call `Var::visible`.
1496    ///
1497    /// # Errors
1498    ///
1499    /// The call will return an error:
1500    /// 1. If `name` does not refer to a valid [`SystemVars`] field.
1501    /// 2. If `values` does not represent a valid [`SystemVars`] value for
1502    ///    `name`.
1503    pub fn is_default(&self, name: &str, input: VarInput) -> Result<bool, VarError> {
1504        self.vars
1505            .get(UncasedStr::new(name))
1506            .ok_or_else(|| VarError::UnknownParameter(name.into()))
1507            .and_then(|v| v.is_default(input))
1508    }
1509
1510    /// Sets the configuration parameter named `name` to the value represented
1511    /// by `input`.
1512    ///
1513    /// Like with [`SystemVars::get`], configuration parameters are matched case
1514    /// insensitively. If `input` is not valid, as determined by the underlying
1515    /// configuration parameter, or if the named configuration parameter does
1516    /// not exist, an error is returned.
1517    ///
1518    /// Return a `bool` value indicating whether the [`Var`] identified by
1519    /// `name` was modified by this call (it won't be if it already had the
1520    /// given `input`).
1521    ///
1522    /// Note that this function does not check that the access variable should
1523    /// be visible because of other settings or users. Before or after accessing
1524    /// this method, you should call `Var::visible`.
1525    ///
1526    /// # Errors
1527    ///
1528    /// The call will return an error:
1529    /// 1. If `name` does not refer to a valid [`SystemVars`] field.
1530    /// 2. If `input` does not represent a valid [`SystemVars`] value for
1531    ///    `name`.
1532    pub fn set(&mut self, name: &str, input: VarInput) -> Result<bool, VarError> {
1533        let result = self
1534            .vars
1535            .get_mut(UncasedStr::new(name))
1536            .ok_or_else(|| VarError::UnknownParameter(name.into()))
1537            .and_then(|v| v.set(input))?;
1538        Ok(result)
1539    }
1540
1541    /// Parses the configuration parameter value represented by `input` named
1542    /// `name`.
1543    ///
1544    /// Like with [`SystemVars::get`], configuration parameters are matched case
1545    /// insensitively. If `input` is not valid, as determined by the underlying
1546    /// configuration parameter, or if the named configuration parameter does
1547    /// not exist, an error is returned.
1548    ///
1549    /// Return a `Box<dyn Value>` that is the result of parsing `input`.
1550    ///
1551    /// Note that this function does not check that the access variable should
1552    /// be visible because of other settings or users. Before or after accessing
1553    /// this method, you should call `Var::visible`.
1554    ///
1555    /// # Errors
1556    ///
1557    /// The call will return an error:
1558    /// 1. If `name` does not refer to a valid [`SystemVars`] field.
1559    /// 2. If `input` does not represent a valid [`SystemVars`] value for
1560    ///    `name`.
1561    pub fn parse(&self, name: &str, input: VarInput) -> Result<Box<dyn Value>, VarError> {
1562        self.vars
1563            .get(UncasedStr::new(name))
1564            .ok_or_else(|| VarError::UnknownParameter(name.into()))
1565            .and_then(|v| v.parse(input))
1566    }
1567
1568    /// Set the default for this variable. This is the value this
1569    /// variable will be be `reset` to. If no default is set, the static default in the
1570    /// variable definition is used instead.
1571    ///
1572    /// Note that this function does not check that the access variable should
1573    /// be visible because of other settings or users. Before or after accessing
1574    /// this method, you should call `Var::visible`.
1575    pub fn set_default(&mut self, name: &str, input: VarInput) -> Result<(), VarError> {
1576        self.vars
1577            .get_mut(UncasedStr::new(name))
1578            .ok_or_else(|| VarError::UnknownParameter(name.into()))
1579            .and_then(|v| v.set_default(input))?;
1580        Ok(())
1581    }
1582
1583    /// Sets the configuration parameter named `name` to its default value.
1584    ///
1585    /// Like with [`SystemVars::get`], configuration parameters are matched case
1586    /// insensitively. If the named configuration parameter does not exist, an
1587    /// error is returned.
1588    ///
1589    /// Return a `bool` value indicating whether the [`Var`] identified by
1590    /// `name` was modified by this call (it won't be if was already reset).
1591    ///
1592    /// Note that this function does not check that the access variable should
1593    /// be visible because of other settings or users. Before or after accessing
1594    /// this method, you should call `Var::visible`.
1595    ///
1596    /// # Errors
1597    ///
1598    /// The call will return an error:
1599    /// 1. If `name` does not refer to a valid [`SystemVars`] field.
1600    pub fn reset(&mut self, name: &str) -> Result<bool, VarError> {
1601        let result = self
1602            .vars
1603            .get_mut(UncasedStr::new(name))
1604            .ok_or_else(|| VarError::UnknownParameter(name.into()))
1605            .map(|v| v.reset())?;
1606        Ok(result)
1607    }
1608
1609    /// Returns a map from each system parameter's name to its default value.
1610    pub fn defaults(&self) -> BTreeMap<String, String> {
1611        self.vars
1612            .iter()
1613            .map(|(name, var)| {
1614                let default = var
1615                    .dynamic_default
1616                    .as_deref()
1617                    .unwrap_or_else(|| var.definition.default_value());
1618                (name.as_str().to_owned(), default.format())
1619            })
1620            .collect()
1621    }
1622
1623    /// Registers a closure that mirrors the value of the given
1624    /// [`VarDefinition`] into out-of-band state.
1625    ///
1626    /// The callback has to be an idempotent read of the passed [`SystemVars`],
1627    /// because we don't promise to only call it when its var actually changed.
1628    /// It runs once right now against the current values, and then again at
1629    /// every catalog commit boundary whose transaction touched a system var
1630    /// (see `Coordinator::apply_catalog_implications` and
1631    /// [`SystemVars::notify_all_callbacks`]). Speculative mutations never
1632    /// trigger it, so an aborted or dry-run transaction leaves the mirror
1633    /// untouched.
1634    ///
1635    /// NOTE: a callback on a `feature_flags!` var won't observe the transient
1636    /// flip that `CatalogState::with_enable_for_item_parsing` performs during
1637    /// item parsing. That flip mutates the value and then restores the prior
1638    /// `Arc` wholesale without re-notifying, so the mirror keeps tracking
1639    /// committed state throughout, which is the contract here. Committed changes
1640    /// to a feature flag (via `ALTER SYSTEM`) still notify like any other var.
1641    pub fn register_callback(
1642        &mut self,
1643        var: &VarDefinition,
1644        callback: Arc<dyn Fn(&SystemVars) + Send + Sync>,
1645    ) {
1646        self.callbacks
1647            .entry(var.name().to_string())
1648            .or_default()
1649            .push(callback);
1650        self.notify_callbacks(var.name());
1651    }
1652
1653    /// Re-runs every registered callback against the current values.
1654    ///
1655    /// This fires all of them, even ones whose var didn't change, which is why
1656    /// callbacks have to be idempotent reads of the passed [`SystemVars`]. See
1657    /// [`SystemVars::register_callback`].
1658    pub fn notify_all_callbacks(&self) {
1659        for callbacks in self.callbacks.values() {
1660            for callback in callbacks {
1661                (callback)(self);
1662            }
1663        }
1664    }
1665
1666    /// Notify any external components interested in this variable.
1667    fn notify_callbacks(&self, name: &str) {
1668        // Get the callbacks interested in this variable.
1669        if let Some(callbacks) = self.callbacks.get(name) {
1670            for callback in callbacks {
1671                (callback)(self);
1672            }
1673        }
1674    }
1675
1676    /// Returns the system default for the [`CLUSTER`] session variable. To know the active cluster
1677    /// for the current session, you must check the [`SessionVars`].
1678    pub fn default_cluster(&self) -> String {
1679        self.expect_value::<String>(&CLUSTER).to_owned()
1680    }
1681
1682    /// Returns the value of the `max_kafka_connections` configuration parameter.
1683    pub fn max_kafka_connections(&self) -> u32 {
1684        *self.expect_value(&MAX_KAFKA_CONNECTIONS)
1685    }
1686
1687    /// Returns the value of the `max_postgres_connections` configuration parameter.
1688    pub fn max_postgres_connections(&self) -> u32 {
1689        *self.expect_value(&MAX_POSTGRES_CONNECTIONS)
1690    }
1691
1692    /// Returns the value of the `max_mysql_connections` configuration parameter.
1693    pub fn max_mysql_connections(&self) -> u32 {
1694        *self.expect_value(&MAX_MYSQL_CONNECTIONS)
1695    }
1696
1697    /// Returns the value of the `max_sql_server_connections` configuration parameter.
1698    pub fn max_sql_server_connections(&self) -> u32 {
1699        *self.expect_value(&MAX_SQL_SERVER_CONNECTIONS)
1700    }
1701
1702    /// Returns the value of the `max_aws_privatelink_connections` configuration parameter.
1703    pub fn max_aws_privatelink_connections(&self) -> u32 {
1704        *self.expect_value(&MAX_AWS_PRIVATELINK_CONNECTIONS)
1705    }
1706
1707    /// Returns the value of the `max_tables` configuration parameter.
1708    pub fn max_tables(&self) -> u32 {
1709        *self.expect_value(&MAX_TABLES)
1710    }
1711
1712    /// Returns the value of the `max_sources` configuration parameter.
1713    pub fn max_sources(&self) -> u32 {
1714        *self.expect_value(&MAX_SOURCES)
1715    }
1716
1717    /// Returns the value of the `max_sinks` configuration parameter.
1718    pub fn max_sinks(&self) -> u32 {
1719        *self.expect_value(&MAX_SINKS)
1720    }
1721
1722    /// Returns the value of the `max_materialized_views` configuration parameter.
1723    pub fn max_materialized_views(&self) -> u32 {
1724        *self.expect_value(&MAX_MATERIALIZED_VIEWS)
1725    }
1726
1727    /// Returns the value of the `max_clusters` configuration parameter.
1728    pub fn max_clusters(&self) -> u32 {
1729        *self.expect_value(&MAX_CLUSTERS)
1730    }
1731
1732    /// Returns the value of the `max_replicas_per_cluster` configuration parameter.
1733    pub fn max_replicas_per_cluster(&self) -> u32 {
1734        *self.expect_value(&MAX_REPLICAS_PER_CLUSTER)
1735    }
1736
1737    /// Returns the value of the `max_credit_consumption_rate` configuration parameter.
1738    pub fn max_credit_consumption_rate(&self) -> Numeric {
1739        *self.expect_value(&MAX_CREDIT_CONSUMPTION_RATE)
1740    }
1741
1742    /// Returns the value of the `max_databases` configuration parameter.
1743    pub fn max_databases(&self) -> u32 {
1744        *self.expect_value(&MAX_DATABASES)
1745    }
1746
1747    /// Returns the value of the `max_schemas_per_database` configuration parameter.
1748    pub fn max_schemas_per_database(&self) -> u32 {
1749        *self.expect_value(&MAX_SCHEMAS_PER_DATABASE)
1750    }
1751
1752    /// Returns the value of the `max_objects_per_schema` configuration parameter.
1753    pub fn max_objects_per_schema(&self) -> u32 {
1754        *self.expect_value(&MAX_OBJECTS_PER_SCHEMA)
1755    }
1756
1757    /// Returns the value of the `max_secrets` configuration parameter.
1758    pub fn max_secrets(&self) -> u32 {
1759        *self.expect_value(&MAX_SECRETS)
1760    }
1761
1762    /// Returns the value of the `max_roles` configuration parameter.
1763    pub fn max_roles(&self) -> u32 {
1764        *self.expect_value(&MAX_ROLES)
1765    }
1766
1767    /// Returns the value of the `max_network_policies` configuration parameter.
1768    pub fn max_network_policies(&self) -> u32 {
1769        *self.expect_value(&MAX_NETWORK_POLICIES)
1770    }
1771
1772    /// Returns the value of the `max_network_policies` configuration parameter.
1773    pub fn max_rules_per_network_policy(&self) -> u32 {
1774        *self.expect_value(&MAX_RULES_PER_NETWORK_POLICY)
1775    }
1776
1777    /// Returns the value of the `max_result_size` configuration parameter.
1778    pub fn max_result_size(&self) -> u64 {
1779        self.expect_value::<ByteSize>(&MAX_RESULT_SIZE).as_bytes()
1780    }
1781
1782    /// Returns the value of the `max_copy_from_row_size` configuration parameter.
1783    pub fn max_copy_from_row_size(&self) -> u64 {
1784        self.expect_value::<ByteSize>(&MAX_COPY_FROM_ROW_SIZE)
1785            .as_bytes()
1786    }
1787
1788    /// Returns the value of the `allowed_cluster_replica_sizes` configuration parameter.
1789    pub fn allowed_cluster_replica_sizes(&self) -> Vec<String> {
1790        self.expect_value::<Vec<Ident>>(&ALLOWED_CLUSTER_REPLICA_SIZES)
1791            .into_iter()
1792            .map(|s| s.as_str().into())
1793            .collect()
1794    }
1795
1796    /// Returns the value of the `max_concurrent_occ_writes` configuration parameter.
1797    pub fn max_concurrent_occ_writes(&self) -> u32 {
1798        *self.expect_value(&MAX_CONCURRENT_OCC_WRITES)
1799    }
1800
1801    /// Returns the value of the `max_occ_retries` configuration parameter.
1802    pub fn max_occ_retries(&self) -> u32 {
1803        *self.expect_value(&MAX_OCC_RETRIES)
1804    }
1805
1806    /// Returns the value of the `default_cluster_replication_factor` configuration parameter.
1807    pub fn default_cluster_replication_factor(&self) -> u32 {
1808        *self.expect_value::<u32>(&DEFAULT_CLUSTER_REPLICATION_FACTOR)
1809    }
1810
1811    pub fn upsert_rocksdb_compaction_style(&self) -> mz_rocksdb_types::config::CompactionStyle {
1812        *self.expect_value(&upsert_rocksdb::UPSERT_ROCKSDB_COMPACTION_STYLE)
1813    }
1814
1815    pub fn upsert_rocksdb_optimize_compaction_memtable_budget(&self) -> usize {
1816        *self.expect_value(&upsert_rocksdb::UPSERT_ROCKSDB_OPTIMIZE_COMPACTION_MEMTABLE_BUDGET)
1817    }
1818
1819    pub fn upsert_rocksdb_level_compaction_dynamic_level_bytes(&self) -> bool {
1820        *self.expect_value(&upsert_rocksdb::UPSERT_ROCKSDB_LEVEL_COMPACTION_DYNAMIC_LEVEL_BYTES)
1821    }
1822
1823    pub fn upsert_rocksdb_universal_compaction_ratio(&self) -> i32 {
1824        *self.expect_value(&upsert_rocksdb::UPSERT_ROCKSDB_UNIVERSAL_COMPACTION_RATIO)
1825    }
1826
1827    pub fn upsert_rocksdb_parallelism(&self) -> Option<i32> {
1828        *self.expect_value(&upsert_rocksdb::UPSERT_ROCKSDB_PARALLELISM)
1829    }
1830
1831    pub fn upsert_rocksdb_compression_type(&self) -> mz_rocksdb_types::config::CompressionType {
1832        *self.expect_value(&upsert_rocksdb::UPSERT_ROCKSDB_COMPRESSION_TYPE)
1833    }
1834
1835    pub fn upsert_rocksdb_bottommost_compression_type(
1836        &self,
1837    ) -> mz_rocksdb_types::config::CompressionType {
1838        *self.expect_value(&upsert_rocksdb::UPSERT_ROCKSDB_BOTTOMMOST_COMPRESSION_TYPE)
1839    }
1840
1841    pub fn upsert_rocksdb_batch_size(&self) -> usize {
1842        *self.expect_value(&upsert_rocksdb::UPSERT_ROCKSDB_BATCH_SIZE)
1843    }
1844
1845    pub fn upsert_rocksdb_retry_duration(&self) -> Duration {
1846        *self.expect_value(&upsert_rocksdb::UPSERT_ROCKSDB_RETRY_DURATION)
1847    }
1848
1849    pub fn upsert_rocksdb_stats_log_interval_seconds(&self) -> u32 {
1850        *self.expect_value(&upsert_rocksdb::UPSERT_ROCKSDB_STATS_LOG_INTERVAL_SECONDS)
1851    }
1852
1853    pub fn upsert_rocksdb_stats_persist_interval_seconds(&self) -> u32 {
1854        *self.expect_value(&upsert_rocksdb::UPSERT_ROCKSDB_STATS_PERSIST_INTERVAL_SECONDS)
1855    }
1856
1857    pub fn upsert_rocksdb_point_lookup_block_cache_size_mb(&self) -> Option<u32> {
1858        *self.expect_value(&upsert_rocksdb::UPSERT_ROCKSDB_POINT_LOOKUP_BLOCK_CACHE_SIZE_MB)
1859    }
1860
1861    pub fn upsert_rocksdb_shrink_allocated_buffers_by_ratio(&self) -> usize {
1862        *self.expect_value(&upsert_rocksdb::UPSERT_ROCKSDB_SHRINK_ALLOCATED_BUFFERS_BY_RATIO)
1863    }
1864
1865    pub fn upsert_rocksdb_write_buffer_manager_cluster_memory_fraction(&self) -> Option<Numeric> {
1866        *self.expect_value(
1867            &upsert_rocksdb::UPSERT_ROCKSDB_WRITE_BUFFER_MANAGER_CLUSTER_MEMORY_FRACTION,
1868        )
1869    }
1870
1871    pub fn upsert_rocksdb_write_buffer_manager_memory_bytes(&self) -> Option<usize> {
1872        *self.expect_value(&upsert_rocksdb::UPSERT_ROCKSDB_WRITE_BUFFER_MANAGER_MEMORY_BYTES)
1873    }
1874
1875    pub fn upsert_rocksdb_write_buffer_manager_allow_stall(&self) -> bool {
1876        *self.expect_value(&upsert_rocksdb::UPSERT_ROCKSDB_WRITE_BUFFER_MANAGER_ALLOW_STALL)
1877    }
1878
1879    pub fn persist_fast_path_limit(&self) -> usize {
1880        *self.expect_value(&PERSIST_FAST_PATH_LIMIT)
1881    }
1882
1883    /// Returns the `pg_source_connect_timeout` configuration parameter.
1884    pub fn pg_source_connect_timeout(&self) -> Duration {
1885        *self.expect_value(&PG_SOURCE_CONNECT_TIMEOUT)
1886    }
1887
1888    /// Returns the `pg_source_tcp_keepalives_retries` configuration parameter.
1889    pub fn pg_source_tcp_keepalives_retries(&self) -> u32 {
1890        *self.expect_value(&PG_SOURCE_TCP_KEEPALIVES_RETRIES)
1891    }
1892
1893    /// Returns the `pg_source_tcp_keepalives_idle` configuration parameter.
1894    pub fn pg_source_tcp_keepalives_idle(&self) -> Duration {
1895        *self.expect_value(&PG_SOURCE_TCP_KEEPALIVES_IDLE)
1896    }
1897
1898    /// Returns the `pg_source_tcp_keepalives_interval` configuration parameter.
1899    pub fn pg_source_tcp_keepalives_interval(&self) -> Duration {
1900        *self.expect_value(&PG_SOURCE_TCP_KEEPALIVES_INTERVAL)
1901    }
1902
1903    /// Returns the `pg_source_tcp_user_timeout` configuration parameter.
1904    pub fn pg_source_tcp_user_timeout(&self) -> Duration {
1905        *self.expect_value(&PG_SOURCE_TCP_USER_TIMEOUT)
1906    }
1907
1908    /// Returns the `pg_source_tcp_configure_server` configuration parameter.
1909    pub fn pg_source_tcp_configure_server(&self) -> bool {
1910        *self.expect_value(&PG_SOURCE_TCP_CONFIGURE_SERVER)
1911    }
1912
1913    /// Returns the `pg_source_snapshot_statement_timeout` configuration parameter.
1914    pub fn pg_source_snapshot_statement_timeout(&self) -> Duration {
1915        *self.expect_value(&PG_SOURCE_SNAPSHOT_STATEMENT_TIMEOUT)
1916    }
1917
1918    /// Returns the `pg_source_wal_sender_timeout` configuration parameter.
1919    pub fn pg_source_wal_sender_timeout(&self) -> Option<Duration> {
1920        *self.expect_value(&PG_SOURCE_WAL_SENDER_TIMEOUT)
1921    }
1922
1923    /// Returns the `pg_source_snapshot_collect_strict_count` configuration parameter.
1924    pub fn pg_source_snapshot_collect_strict_count(&self) -> bool {
1925        *self.expect_value(&PG_SOURCE_SNAPSHOT_COLLECT_STRICT_COUNT)
1926    }
1927
1928    /// Returns the `mysql_source_tcp_keepalive` configuration parameter.
1929    pub fn mysql_source_tcp_keepalive(&self) -> Duration {
1930        *self.expect_value(&MYSQL_SOURCE_TCP_KEEPALIVE)
1931    }
1932
1933    /// Returns the `mysql_source_snapshot_max_execution_time` configuration parameter.
1934    pub fn mysql_source_snapshot_max_execution_time(&self) -> Duration {
1935        *self.expect_value(&MYSQL_SOURCE_SNAPSHOT_MAX_EXECUTION_TIME)
1936    }
1937
1938    /// Returns the `mysql_source_snapshot_lock_wait_timeout` configuration parameter.
1939    pub fn mysql_source_snapshot_lock_wait_timeout(&self) -> Duration {
1940        *self.expect_value(&MYSQL_SOURCE_SNAPSHOT_LOCK_WAIT_TIMEOUT)
1941    }
1942
1943    /// Returns the `mysql_source_snapshot_wait_timeout` configuration parameter.
1944    pub fn mysql_source_snapshot_wait_timeout(&self) -> Duration {
1945        *self.expect_value(&MYSQL_SOURCE_SNAPSHOT_WAIT_TIMEOUT)
1946    }
1947
1948    /// Returns the `mysql_source_connect_timeout` configuration parameter.
1949    pub fn mysql_source_connect_timeout(&self) -> Duration {
1950        *self.expect_value(&MYSQL_SOURCE_CONNECT_TIMEOUT)
1951    }
1952
1953    /// Returns the `ssh_check_interval` configuration parameter.
1954    pub fn ssh_check_interval(&self) -> Duration {
1955        *self.expect_value(&SSH_CHECK_INTERVAL)
1956    }
1957
1958    /// Returns the `ssh_connect_timeout` configuration parameter.
1959    pub fn ssh_connect_timeout(&self) -> Duration {
1960        *self.expect_value(&SSH_CONNECT_TIMEOUT)
1961    }
1962
1963    /// Returns the `ssh_keepalives_idle` configuration parameter.
1964    pub fn ssh_keepalives_idle(&self) -> Duration {
1965        *self.expect_value(&SSH_KEEPALIVES_IDLE)
1966    }
1967
1968    /// Returns the `kafka_socket_keepalive` configuration parameter.
1969    pub fn kafka_socket_keepalive(&self) -> bool {
1970        *self.expect_value(&KAFKA_SOCKET_KEEPALIVE)
1971    }
1972
1973    /// Returns the `kafka_socket_timeout` configuration parameter.
1974    pub fn kafka_socket_timeout(&self) -> Option<Duration> {
1975        *self.expect_value(&KAFKA_SOCKET_TIMEOUT)
1976    }
1977
1978    /// Returns the `kafka_transaction_timeout` configuration parameter.
1979    pub fn kafka_transaction_timeout(&self) -> Duration {
1980        *self.expect_value(&KAFKA_TRANSACTION_TIMEOUT)
1981    }
1982
1983    /// Returns the `kafka_socket_connection_setup_timeout` configuration parameter.
1984    pub fn kafka_socket_connection_setup_timeout(&self) -> Duration {
1985        *self.expect_value(&KAFKA_SOCKET_CONNECTION_SETUP_TIMEOUT)
1986    }
1987
1988    /// Returns the `kafka_fetch_metadata_timeout` configuration parameter.
1989    pub fn kafka_fetch_metadata_timeout(&self) -> Duration {
1990        *self.expect_value(&KAFKA_FETCH_METADATA_TIMEOUT)
1991    }
1992
1993    /// Returns the `kafka_progress_record_fetch_timeout` configuration parameter.
1994    pub fn kafka_progress_record_fetch_timeout(&self) -> Option<Duration> {
1995        *self.expect_value(&KAFKA_PROGRESS_RECORD_FETCH_TIMEOUT)
1996    }
1997
1998    /// Returns the `crdb_connect_timeout` configuration parameter.
1999    pub fn crdb_connect_timeout(&self) -> Duration {
2000        *self.expect_config_value(UncasedStr::new(
2001            mz_persist_client::cfg::CRDB_CONNECT_TIMEOUT.name(),
2002        ))
2003    }
2004
2005    /// Returns the `crdb_tcp_user_timeout` configuration parameter.
2006    pub fn crdb_tcp_user_timeout(&self) -> Duration {
2007        *self.expect_config_value(UncasedStr::new(
2008            mz_persist_client::cfg::CRDB_TCP_USER_TIMEOUT.name(),
2009        ))
2010    }
2011
2012    /// Returns the `crdb_keepalives_idle` configuration parameter.
2013    pub fn crdb_keepalives_idle(&self) -> Duration {
2014        *self.expect_config_value(UncasedStr::new(
2015            mz_persist_client::cfg::CRDB_KEEPALIVES_IDLE.name(),
2016        ))
2017    }
2018
2019    /// Returns the `crdb_keepalives_interval` configuration parameter.
2020    pub fn crdb_keepalives_interval(&self) -> Duration {
2021        *self.expect_config_value(UncasedStr::new(
2022            mz_persist_client::cfg::CRDB_KEEPALIVES_INTERVAL.name(),
2023        ))
2024    }
2025
2026    /// Returns the `crdb_keepalives_retries` configuration parameter.
2027    pub fn crdb_keepalives_retries(&self) -> u32 {
2028        *self.expect_config_value(UncasedStr::new(
2029            mz_persist_client::cfg::CRDB_KEEPALIVES_RETRIES.name(),
2030        ))
2031    }
2032
2033    /// Returns the `storage_dataflow_max_inflight_bytes` configuration parameter.
2034    pub fn storage_dataflow_max_inflight_bytes(&self) -> Option<usize> {
2035        *self.expect_value(&STORAGE_DATAFLOW_MAX_INFLIGHT_BYTES)
2036    }
2037
2038    /// Returns the `storage_dataflow_max_inflight_bytes_to_cluster_size_fraction` configuration parameter.
2039    pub fn storage_dataflow_max_inflight_bytes_to_cluster_size_fraction(&self) -> Option<Numeric> {
2040        *self.expect_value(&STORAGE_DATAFLOW_MAX_INFLIGHT_BYTES_TO_CLUSTER_SIZE_FRACTION)
2041    }
2042
2043    /// Returns the `storage_shrink_upsert_unused_buffers_by_ratio` configuration parameter.
2044    pub fn storage_shrink_upsert_unused_buffers_by_ratio(&self) -> usize {
2045        *self.expect_value(&STORAGE_SHRINK_UPSERT_UNUSED_BUFFERS_BY_RATIO)
2046    }
2047
2048    /// Returns the `storage_dataflow_max_inflight_bytes_disk_only` configuration parameter.
2049    pub fn storage_dataflow_max_inflight_bytes_disk_only(&self) -> bool {
2050        *self.expect_value(&STORAGE_DATAFLOW_MAX_INFLIGHT_BYTES_DISK_ONLY)
2051    }
2052
2053    /// Returns the `storage_statistics_interval` configuration parameter.
2054    pub fn storage_statistics_interval(&self) -> Duration {
2055        *self.expect_value(&STORAGE_STATISTICS_INTERVAL)
2056    }
2057
2058    /// Returns the `storage_statistics_collection_interval` configuration parameter.
2059    pub fn storage_statistics_collection_interval(&self) -> Duration {
2060        *self.expect_value(&STORAGE_STATISTICS_COLLECTION_INTERVAL)
2061    }
2062
2063    /// Returns the `storage_record_source_sink_namespaced_errors` configuration parameter.
2064    pub fn storage_record_source_sink_namespaced_errors(&self) -> bool {
2065        *self.expect_value(&STORAGE_RECORD_SOURCE_SINK_NAMESPACED_ERRORS)
2066    }
2067
2068    /// Returns the `persist_stats_filter_enabled` configuration parameter.
2069    pub fn persist_stats_filter_enabled(&self) -> bool {
2070        *self.expect_config_value(UncasedStr::new(
2071            mz_persist_client::stats::STATS_FILTER_ENABLED.name(),
2072        ))
2073    }
2074
2075    pub fn scram_iterations(&self) -> NonZeroU32 {
2076        *self.expect_value(&SCRAM_ITERATIONS)
2077    }
2078
2079    /// Computes an update for every dyncfg from its configured value.
2080    ///
2081    /// This does not touch our own [`ConfigSet`]. Callers that need this
2082    /// process's dyncfgs to reflect the result want [`Self::sync_dyncfgs`].
2083    pub fn dyncfg_updates(&self) -> ConfigUpdates {
2084        let mut updates = ConfigUpdates::default();
2085        for entry in self.dyncfgs.entries() {
2086            let name = UncasedStr::new(entry.name());
2087            let val = match entry.val() {
2088                ConfigVal::Bool(_) => ConfigVal::from(*self.expect_config_value::<bool>(name)),
2089                ConfigVal::U32(_) => ConfigVal::from(*self.expect_config_value::<u32>(name)),
2090                ConfigVal::Usize(_) => ConfigVal::from(*self.expect_config_value::<usize>(name)),
2091                ConfigVal::OptUsize(_) => {
2092                    ConfigVal::from(*self.expect_config_value::<Option<usize>>(name))
2093                }
2094                ConfigVal::F64(_) => ConfigVal::from(*self.expect_config_value::<f64>(name)),
2095                ConfigVal::String(_) => {
2096                    ConfigVal::from(self.expect_config_value::<String>(name).clone())
2097                }
2098                ConfigVal::OptString(_) => {
2099                    ConfigVal::from(self.expect_config_value::<Option<String>>(name).clone())
2100                }
2101                ConfigVal::Duration(_) => {
2102                    ConfigVal::from(*self.expect_config_value::<Duration>(name))
2103                }
2104                ConfigVal::Json(_) => {
2105                    ConfigVal::from(self.expect_config_value::<serde_json::Value>(name).clone())
2106                }
2107            };
2108            updates.add_dynamic(entry.name(), val);
2109        }
2110        updates
2111    }
2112
2113    /// Applies the configured dyncfg values to our own [`ConfigSet`], and
2114    /// returns them.
2115    ///
2116    /// Two callers own keeping this process's dyncfgs in step with the
2117    /// catalog: catalog open, and every durable system-config change. Everyone
2118    /// else only forwards the updates elsewhere and wants
2119    /// [`Self::dyncfg_updates`] instead.
2120    pub fn sync_dyncfgs(&self) -> ConfigUpdates {
2121        let updates = self.dyncfg_updates();
2122        updates.apply(&self.dyncfgs);
2123        updates
2124    }
2125
2126    /// Returns the `metrics_retention` configuration parameter.
2127    pub fn metrics_retention(&self) -> Duration {
2128        *self.expect_value(&METRICS_RETENTION)
2129    }
2130
2131    /// Returns the `unsafe_mock_audit_event_timestamp` configuration parameter.
2132    pub fn unsafe_mock_audit_event_timestamp(&self) -> Option<mz_repr::Timestamp> {
2133        *self.expect_value(&UNSAFE_MOCK_AUDIT_EVENT_TIMESTAMP)
2134    }
2135
2136    /// Returns the `enable_rbac_checks` configuration parameter.
2137    pub fn enable_rbac_checks(&self) -> bool {
2138        *self.expect_value(&ENABLE_RBAC_CHECKS)
2139    }
2140
2141    /// Returns the `max_connections` configuration parameter.
2142    pub fn max_connections(&self) -> u32 {
2143        *self.expect_value(&MAX_CONNECTIONS)
2144    }
2145
2146    pub fn default_network_policy_name(&self) -> String {
2147        self.expect_value::<String>(&NETWORK_POLICY).clone()
2148    }
2149
2150    /// Returns the `superuser_reserved_connections` configuration parameter.
2151    pub fn superuser_reserved_connections(&self) -> u32 {
2152        *self.expect_value(&SUPERUSER_RESERVED_CONNECTIONS)
2153    }
2154
2155    pub fn keep_n_source_status_history_entries(&self) -> usize {
2156        *self.expect_value(&KEEP_N_SOURCE_STATUS_HISTORY_ENTRIES)
2157    }
2158
2159    pub fn keep_n_sink_status_history_entries(&self) -> usize {
2160        *self.expect_value(&KEEP_N_SINK_STATUS_HISTORY_ENTRIES)
2161    }
2162
2163    pub fn keep_n_privatelink_status_history_entries(&self) -> usize {
2164        *self.expect_value(&KEEP_N_PRIVATELINK_STATUS_HISTORY_ENTRIES)
2165    }
2166
2167    pub fn replica_status_history_retention_window(&self) -> Duration {
2168        *self.expect_value(&REPLICA_STATUS_HISTORY_RETENTION_WINDOW)
2169    }
2170
2171    /// Returns the `enable_storage_shard_finalization` configuration parameter.
2172    pub fn enable_storage_shard_finalization(&self) -> bool {
2173        *self.expect_value(&ENABLE_STORAGE_SHARD_FINALIZATION)
2174    }
2175
2176    /// Returns the `enable_default_connection_validation` configuration parameter.
2177    pub fn enable_default_connection_validation(&self) -> bool {
2178        *self.expect_value(&ENABLE_DEFAULT_CONNECTION_VALIDATION)
2179    }
2180
2181    /// Returns the `default_timestamp_interval` configuration parameter.
2182    pub fn default_timestamp_interval(&self) -> Duration {
2183        *self.expect_value(&DEFAULT_TIMESTAMP_INTERVAL)
2184    }
2185
2186    /// Returns the `min_timestamp_interval` configuration parameter.
2187    pub fn min_timestamp_interval(&self) -> Duration {
2188        *self.expect_value(&MIN_TIMESTAMP_INTERVAL)
2189    }
2190    /// Returns the `max_timestamp_interval` configuration parameter.
2191    pub fn max_timestamp_interval(&self) -> Duration {
2192        *self.expect_value(&MAX_TIMESTAMP_INTERVAL)
2193    }
2194
2195    pub fn logging_filter(&self) -> CloneableEnvFilter {
2196        self.expect_value::<CloneableEnvFilter>(&LOGGING_FILTER)
2197            .clone()
2198    }
2199
2200    pub fn opentelemetry_filter(&self) -> CloneableEnvFilter {
2201        self.expect_value::<CloneableEnvFilter>(&OPENTELEMETRY_FILTER)
2202            .clone()
2203    }
2204
2205    pub fn logging_filter_defaults(&self) -> Vec<SerializableDirective> {
2206        self.expect_value::<Vec<SerializableDirective>>(&LOGGING_FILTER_DEFAULTS)
2207            .clone()
2208    }
2209
2210    pub fn opentelemetry_filter_defaults(&self) -> Vec<SerializableDirective> {
2211        self.expect_value::<Vec<SerializableDirective>>(&OPENTELEMETRY_FILTER_DEFAULTS)
2212            .clone()
2213    }
2214
2215    pub fn sentry_filters(&self) -> Vec<SerializableDirective> {
2216        self.expect_value::<Vec<SerializableDirective>>(&SENTRY_FILTERS)
2217            .clone()
2218    }
2219
2220    pub fn webhooks_secrets_caching_ttl_secs(&self) -> usize {
2221        *self.expect_value(&WEBHOOKS_SECRETS_CACHING_TTL_SECS)
2222    }
2223
2224    pub fn coord_slow_message_warn_threshold(&self) -> Duration {
2225        *self.expect_value(&COORD_SLOW_MESSAGE_WARN_THRESHOLD)
2226    }
2227
2228    pub fn grpc_client_http2_keep_alive_interval(&self) -> Duration {
2229        *self.expect_value(&grpc_client::HTTP2_KEEP_ALIVE_INTERVAL)
2230    }
2231
2232    pub fn grpc_client_http2_keep_alive_timeout(&self) -> Duration {
2233        *self.expect_value(&grpc_client::HTTP2_KEEP_ALIVE_TIMEOUT)
2234    }
2235
2236    pub fn grpc_connect_timeout(&self) -> Duration {
2237        *self.expect_value(&grpc_client::CONNECT_TIMEOUT)
2238    }
2239
2240    pub fn cluster_multi_process_replica_az_affinity_weight(&self) -> Option<i32> {
2241        *self.expect_value(&cluster_scheduling::CLUSTER_MULTI_PROCESS_REPLICA_AZ_AFFINITY_WEIGHT)
2242    }
2243
2244    pub fn cluster_soften_replication_anti_affinity(&self) -> bool {
2245        *self.expect_value(&cluster_scheduling::CLUSTER_SOFTEN_REPLICATION_ANTI_AFFINITY)
2246    }
2247
2248    pub fn cluster_soften_replication_anti_affinity_weight(&self) -> i32 {
2249        *self.expect_value(&cluster_scheduling::CLUSTER_SOFTEN_REPLICATION_ANTI_AFFINITY_WEIGHT)
2250    }
2251
2252    pub fn cluster_enable_topology_spread(&self) -> bool {
2253        *self.expect_value(&cluster_scheduling::CLUSTER_ENABLE_TOPOLOGY_SPREAD)
2254    }
2255
2256    pub fn cluster_topology_spread_ignore_non_singular_scale(&self) -> bool {
2257        *self.expect_value(&cluster_scheduling::CLUSTER_TOPOLOGY_SPREAD_IGNORE_NON_SINGULAR_SCALE)
2258    }
2259
2260    pub fn cluster_topology_spread_max_skew(&self) -> i32 {
2261        *self.expect_value(&cluster_scheduling::CLUSTER_TOPOLOGY_SPREAD_MAX_SKEW)
2262    }
2263
2264    pub fn cluster_topology_spread_set_min_domains(&self) -> Option<i32> {
2265        *self.expect_value(&cluster_scheduling::CLUSTER_TOPOLOGY_SPREAD_MIN_DOMAINS)
2266    }
2267
2268    pub fn cluster_topology_spread_soft(&self) -> bool {
2269        *self.expect_value(&cluster_scheduling::CLUSTER_TOPOLOGY_SPREAD_SOFT)
2270    }
2271
2272    pub fn cluster_soften_az_affinity(&self) -> bool {
2273        *self.expect_value(&cluster_scheduling::CLUSTER_SOFTEN_AZ_AFFINITY)
2274    }
2275
2276    pub fn cluster_soften_az_affinity_weight(&self) -> i32 {
2277        *self.expect_value(&cluster_scheduling::CLUSTER_SOFTEN_AZ_AFFINITY_WEIGHT)
2278    }
2279
2280    pub fn cluster_alter_check_ready_interval(&self) -> Duration {
2281        *self.expect_value(&cluster_scheduling::CLUSTER_ALTER_CHECK_READY_INTERVAL)
2282    }
2283
2284    pub fn cluster_security_context_enabled(&self) -> bool {
2285        *self.expect_value(&cluster_scheduling::CLUSTER_SECURITY_CONTEXT_ENABLED)
2286    }
2287
2288    pub fn cluster_refresh_mv_compaction_estimate(&self) -> Duration {
2289        *self.expect_value(&cluster_scheduling::CLUSTER_REFRESH_MV_COMPACTION_ESTIMATE)
2290    }
2291
2292    /// Returns the `privatelink_status_update_quota_per_minute` configuration parameter.
2293    pub fn privatelink_status_update_quota_per_minute(&self) -> u32 {
2294        *self.expect_value(&PRIVATELINK_STATUS_UPDATE_QUOTA_PER_MINUTE)
2295    }
2296
2297    pub fn statement_logging_target_data_rate(&self) -> Option<usize> {
2298        *self.expect_value(&STATEMENT_LOGGING_TARGET_DATA_RATE)
2299    }
2300
2301    pub fn statement_logging_max_data_credit(&self) -> Option<usize> {
2302        *self.expect_value(&STATEMENT_LOGGING_MAX_DATA_CREDIT)
2303    }
2304
2305    /// Returns the `statement_logging_max_sample_rate` configuration parameter.
2306    pub fn statement_logging_max_sample_rate(&self) -> Numeric {
2307        *self.expect_value(&STATEMENT_LOGGING_MAX_SAMPLE_RATE)
2308    }
2309
2310    /// Returns the `statement_logging_default_sample_rate` configuration parameter.
2311    pub fn statement_logging_default_sample_rate(&self) -> Numeric {
2312        *self.expect_value(&STATEMENT_LOGGING_DEFAULT_SAMPLE_RATE)
2313    }
2314
2315    /// Returns the `enable_internal_statement_logging` configuration parameter.
2316    pub fn enable_internal_statement_logging(&self) -> bool {
2317        *self.expect_value(&ENABLE_INTERNAL_STATEMENT_LOGGING)
2318    }
2319
2320    /// Returns the `enable_statement_arrival_logging` configuration parameter.
2321    pub fn enable_statement_arrival_logging(&self) -> bool {
2322        *self.expect_value(&ENABLE_STATEMENT_ARRIVAL_LOGGING)
2323    }
2324
2325    /// Returns the `enable_extended_protocol_implicit_transaction` configuration
2326    /// parameter.
2327    pub fn enable_extended_protocol_implicit_transaction(&self) -> bool {
2328        *self.expect_value(&ENABLE_EXTENDED_PROTOCOL_IMPLICIT_TRANSACTION)
2329    }
2330
2331    /// Returns the `optimizer_stats_timeout` configuration parameter.
2332    pub fn optimizer_stats_timeout(&self) -> Duration {
2333        *self.expect_value(&OPTIMIZER_STATS_TIMEOUT)
2334    }
2335
2336    /// Returns the `optimizer_oneshot_stats_timeout` configuration parameter.
2337    pub fn optimizer_oneshot_stats_timeout(&self) -> Duration {
2338        *self.expect_value(&OPTIMIZER_ONESHOT_STATS_TIMEOUT)
2339    }
2340
2341    /// Returns the `webhook_concurrent_request_limit` configuration parameter.
2342    pub fn webhook_concurrent_request_limit(&self) -> usize {
2343        *self.expect_value(&WEBHOOK_CONCURRENT_REQUEST_LIMIT)
2344    }
2345
2346    /// Returns the `pg_timestamp_oracle_connection_pool_max_size` configuration parameter.
2347    pub fn pg_timestamp_oracle_connection_pool_max_size(&self) -> usize {
2348        *self.expect_value(&PG_TIMESTAMP_ORACLE_CONNECTION_POOL_MAX_SIZE)
2349    }
2350
2351    /// Returns the `pg_timestamp_oracle_connection_pool_max_wait` configuration parameter.
2352    pub fn pg_timestamp_oracle_connection_pool_max_wait(&self) -> Option<Duration> {
2353        *self.expect_value(&PG_TIMESTAMP_ORACLE_CONNECTION_POOL_MAX_WAIT)
2354    }
2355
2356    /// Returns the `pg_timestamp_oracle_connection_pool_ttl` configuration parameter.
2357    pub fn pg_timestamp_oracle_connection_pool_ttl(&self) -> Duration {
2358        *self.expect_value(&PG_TIMESTAMP_ORACLE_CONNECTION_POOL_TTL)
2359    }
2360
2361    /// Returns the `pg_timestamp_oracle_connection_pool_ttl_stagger` configuration parameter.
2362    pub fn pg_timestamp_oracle_connection_pool_ttl_stagger(&self) -> Duration {
2363        *self.expect_value(&PG_TIMESTAMP_ORACLE_CONNECTION_POOL_TTL_STAGGER)
2364    }
2365
2366    /// Returns the `user_storage_managed_collections_batch_duration` configuration parameter.
2367    pub fn user_storage_managed_collections_batch_duration(&self) -> Duration {
2368        *self.expect_value(&USER_STORAGE_MANAGED_COLLECTIONS_BATCH_DURATION)
2369    }
2370
2371    pub fn force_source_table_syntax(&self) -> bool {
2372        *self.expect_value(&FORCE_SOURCE_TABLE_SYNTAX)
2373    }
2374
2375    pub fn optimizer_e2e_latency_warning_threshold(&self) -> Duration {
2376        *self.expect_value(&OPTIMIZER_E2E_LATENCY_WARNING_THRESHOLD)
2377    }
2378
2379    /// Returns whether the named variable is a controller configuration parameter.
2380    pub fn is_controller_config_var(&self, name: &str) -> bool {
2381        self.is_dyncfg_var(name)
2382    }
2383
2384    /// Returns whether the named variable is a compute configuration parameter
2385    /// (things that go in `ComputeParameters` and are sent to replicas via `UpdateConfiguration`
2386    /// commands).
2387    pub fn is_compute_config_var(&self, name: &str) -> bool {
2388        name == MAX_RESULT_SIZE.name() || self.is_dyncfg_var(name) || is_tracing_var(name)
2389    }
2390
2391    /// Returns whether the named variable is a metrics configuration parameter
2392    pub fn is_metrics_config_var(&self, name: &str) -> bool {
2393        self.is_dyncfg_var(name)
2394    }
2395
2396    /// Returns whether the named variable is a storage configuration parameter.
2397    pub fn is_storage_config_var(&self, name: &str) -> bool {
2398        name == PG_SOURCE_CONNECT_TIMEOUT.name()
2399            || name == PG_SOURCE_TCP_KEEPALIVES_IDLE.name()
2400            || name == PG_SOURCE_TCP_KEEPALIVES_INTERVAL.name()
2401            || name == PG_SOURCE_TCP_KEEPALIVES_RETRIES.name()
2402            || name == PG_SOURCE_TCP_USER_TIMEOUT.name()
2403            || name == PG_SOURCE_TCP_CONFIGURE_SERVER.name()
2404            || name == PG_SOURCE_SNAPSHOT_STATEMENT_TIMEOUT.name()
2405            || name == PG_SOURCE_WAL_SENDER_TIMEOUT.name()
2406            || name == PG_SOURCE_SNAPSHOT_COLLECT_STRICT_COUNT.name()
2407            || name == MYSQL_SOURCE_TCP_KEEPALIVE.name()
2408            || name == MYSQL_SOURCE_SNAPSHOT_MAX_EXECUTION_TIME.name()
2409            || name == MYSQL_SOURCE_SNAPSHOT_LOCK_WAIT_TIMEOUT.name()
2410            || name == MYSQL_SOURCE_SNAPSHOT_WAIT_TIMEOUT.name()
2411            || name == MYSQL_SOURCE_CONNECT_TIMEOUT.name()
2412            || name == ENABLE_STORAGE_SHARD_FINALIZATION.name()
2413            || name == SSH_CHECK_INTERVAL.name()
2414            || name == SSH_CONNECT_TIMEOUT.name()
2415            || name == SSH_KEEPALIVES_IDLE.name()
2416            || name == KAFKA_SOCKET_KEEPALIVE.name()
2417            || name == KAFKA_SOCKET_TIMEOUT.name()
2418            || name == KAFKA_TRANSACTION_TIMEOUT.name()
2419            || name == KAFKA_SOCKET_CONNECTION_SETUP_TIMEOUT.name()
2420            || name == KAFKA_FETCH_METADATA_TIMEOUT.name()
2421            || name == KAFKA_PROGRESS_RECORD_FETCH_TIMEOUT.name()
2422            || name == STORAGE_DATAFLOW_MAX_INFLIGHT_BYTES.name()
2423            || name == STORAGE_DATAFLOW_MAX_INFLIGHT_BYTES_TO_CLUSTER_SIZE_FRACTION.name()
2424            || name == STORAGE_DATAFLOW_MAX_INFLIGHT_BYTES_DISK_ONLY.name()
2425            || name == STORAGE_SHRINK_UPSERT_UNUSED_BUFFERS_BY_RATIO.name()
2426            || name == STORAGE_RECORD_SOURCE_SINK_NAMESPACED_ERRORS.name()
2427            || name == STORAGE_STATISTICS_INTERVAL.name()
2428            || name == STORAGE_STATISTICS_COLLECTION_INTERVAL.name()
2429            || name == USER_STORAGE_MANAGED_COLLECTIONS_BATCH_DURATION.name()
2430            || is_upsert_rocksdb_config_var(name)
2431            || self.is_dyncfg_var(name)
2432            || is_tracing_var(name)
2433    }
2434
2435    /// Returns whether the named variable is a dyncfg configuration parameter.
2436    fn is_dyncfg_var(&self, name: &str) -> bool {
2437        self.dyncfgs.entries().any(|e| name == e.name())
2438    }
2439}
2440
2441pub fn is_tracing_var(name: &str) -> bool {
2442    name == LOGGING_FILTER.name()
2443        || name == LOGGING_FILTER_DEFAULTS.name()
2444        || name == OPENTELEMETRY_FILTER.name()
2445        || name == OPENTELEMETRY_FILTER_DEFAULTS.name()
2446        || name == SENTRY_FILTERS.name()
2447}
2448
2449/// Returns whether the named variable is a caching configuration parameter.
2450pub fn is_secrets_caching_var(name: &str) -> bool {
2451    name == WEBHOOKS_SECRETS_CACHING_TTL_SECS.name()
2452}
2453
2454fn is_upsert_rocksdb_config_var(name: &str) -> bool {
2455    name == upsert_rocksdb::UPSERT_ROCKSDB_COMPACTION_STYLE.name()
2456        || name == upsert_rocksdb::UPSERT_ROCKSDB_OPTIMIZE_COMPACTION_MEMTABLE_BUDGET.name()
2457        || name == upsert_rocksdb::UPSERT_ROCKSDB_LEVEL_COMPACTION_DYNAMIC_LEVEL_BYTES.name()
2458        || name == upsert_rocksdb::UPSERT_ROCKSDB_UNIVERSAL_COMPACTION_RATIO.name()
2459        || name == upsert_rocksdb::UPSERT_ROCKSDB_PARALLELISM.name()
2460        || name == upsert_rocksdb::UPSERT_ROCKSDB_COMPRESSION_TYPE.name()
2461        || name == upsert_rocksdb::UPSERT_ROCKSDB_BOTTOMMOST_COMPRESSION_TYPE.name()
2462        || name == upsert_rocksdb::UPSERT_ROCKSDB_BATCH_SIZE.name()
2463        || name == upsert_rocksdb::UPSERT_ROCKSDB_STATS_LOG_INTERVAL_SECONDS.name()
2464        || name == upsert_rocksdb::UPSERT_ROCKSDB_STATS_PERSIST_INTERVAL_SECONDS.name()
2465        || name == upsert_rocksdb::UPSERT_ROCKSDB_POINT_LOOKUP_BLOCK_CACHE_SIZE_MB.name()
2466        || name == upsert_rocksdb::UPSERT_ROCKSDB_SHRINK_ALLOCATED_BUFFERS_BY_RATIO.name()
2467}
2468
2469/// Returns whether the named variable is a (Postgres/CRDB) timestamp oracle
2470/// configuration parameter.
2471pub fn is_timestamp_oracle_config_var(name: &str) -> bool {
2472    name == PG_TIMESTAMP_ORACLE_CONNECTION_POOL_MAX_SIZE.name()
2473        || name == PG_TIMESTAMP_ORACLE_CONNECTION_POOL_MAX_WAIT.name()
2474        || name == PG_TIMESTAMP_ORACLE_CONNECTION_POOL_TTL.name()
2475        || name == PG_TIMESTAMP_ORACLE_CONNECTION_POOL_TTL_STAGGER.name()
2476        || name == CRDB_CONNECT_TIMEOUT.name()
2477        || name == CRDB_TCP_USER_TIMEOUT.name()
2478        || name == CRDB_KEEPALIVES_IDLE.name()
2479        || name == CRDB_KEEPALIVES_INTERVAL.name()
2480        || name == CRDB_KEEPALIVES_RETRIES.name()
2481        || name == mz_adapter_types::dyncfgs::PG_TIMESTAMP_ORACLE_STATEMENT_TIMEOUT.name()
2482}
2483
2484/// Returns whether the named variable is a cluster scheduling config
2485pub fn is_cluster_scheduling_var(name: &str) -> bool {
2486    name == cluster_scheduling::CLUSTER_MULTI_PROCESS_REPLICA_AZ_AFFINITY_WEIGHT.name()
2487        || name == cluster_scheduling::CLUSTER_SOFTEN_REPLICATION_ANTI_AFFINITY.name()
2488        || name == cluster_scheduling::CLUSTER_SOFTEN_REPLICATION_ANTI_AFFINITY_WEIGHT.name()
2489        || name == cluster_scheduling::CLUSTER_ENABLE_TOPOLOGY_SPREAD.name()
2490        || name == cluster_scheduling::CLUSTER_TOPOLOGY_SPREAD_IGNORE_NON_SINGULAR_SCALE.name()
2491        || name == cluster_scheduling::CLUSTER_TOPOLOGY_SPREAD_MAX_SKEW.name()
2492        || name == cluster_scheduling::CLUSTER_TOPOLOGY_SPREAD_MIN_DOMAINS.name()
2493        || name == cluster_scheduling::CLUSTER_TOPOLOGY_SPREAD_SOFT.name()
2494        || name == cluster_scheduling::CLUSTER_SOFTEN_AZ_AFFINITY.name()
2495        || name == cluster_scheduling::CLUSTER_SOFTEN_AZ_AFFINITY_WEIGHT.name()
2496}
2497
2498/// Returns whether the named variable is an HTTP server related config var.
2499pub fn is_http_config_var(name: &str) -> bool {
2500    name == WEBHOOK_CONCURRENT_REQUEST_LIMIT.name()
2501}
2502
2503/// Set of [`SystemVar`]s that can also get set at a per-Session level.
2504///
2505/// TODO(parkmycar): Instead of a separate list, make this a field on VarDefinition.
2506static SESSION_SYSTEM_VARS: LazyLock<BTreeMap<&'static UncasedStr, &'static VarDefinition>> =
2507    LazyLock::new(|| {
2508        [
2509            &APPLICATION_NAME,
2510            &CLIENT_ENCODING,
2511            &CLIENT_MIN_MESSAGES,
2512            &CLUSTER,
2513            &CLUSTER_REPLICA,
2514            &DEFAULT_CLUSTER_REPLICATION_FACTOR,
2515            &CURRENT_OBJECT_MISSING_WARNINGS,
2516            &DATABASE,
2517            &DATE_STYLE,
2518            &EXTRA_FLOAT_DIGITS,
2519            &INTEGER_DATETIMES,
2520            &INTERVAL_STYLE,
2521            &REAL_TIME_RECENCY_TIMEOUT,
2522            &SEARCH_PATH,
2523            &STANDARD_CONFORMING_STRINGS,
2524            &STATEMENT_TIMEOUT,
2525            &IDLE_IN_TRANSACTION_SESSION_TIMEOUT,
2526            &TIMEZONE,
2527            &TRANSACTION_ISOLATION,
2528            &MAX_QUERY_RESULT_SIZE,
2529        ]
2530        .into_iter()
2531        .map(|var| (UncasedStr::new(var.name()), var))
2532        .collect()
2533    });
2534
2535// Provides a wrapper to express that a particular `ServerVar` is meant to be used as a feature
2536/// flag.
2537#[derive(Debug)]
2538pub struct FeatureFlag {
2539    pub flag: &'static VarDefinition,
2540    pub feature_desc: &'static str,
2541}
2542
2543impl FeatureFlag {
2544    /// Returns whether the feature flag is enabled in the provided `system_vars`.
2545    pub fn enabled(&'static self, system_vars: &SystemVars) -> bool {
2546        *system_vars.expect_value::<bool>(self.flag)
2547    }
2548
2549    /// Returns an error unless the feature flag is enabled in the provided
2550    /// `system_vars`.
2551    pub fn require(&'static self, system_vars: &SystemVars) -> Result<(), VarError> {
2552        match self.enabled(system_vars) {
2553            true => Ok(()),
2554            false => Err(VarError::RequiresFeatureFlag { feature_flag: self }),
2555        }
2556    }
2557}
2558
2559impl PartialEq for FeatureFlag {
2560    fn eq(&self, other: &FeatureFlag) -> bool {
2561        self.flag.name() == other.flag.name()
2562    }
2563}
2564
2565impl Eq for FeatureFlag {}
2566
2567impl Var for MzVersion {
2568    fn name(&self) -> &'static str {
2569        MZ_VERSION_NAME.as_str()
2570    }
2571
2572    fn value(&self) -> String {
2573        self.build_info
2574            .human_version(self.helm_chart_version.clone())
2575    }
2576
2577    fn description(&self) -> &'static str {
2578        "Shows the Materialize server version (Materialize)."
2579    }
2580
2581    fn type_name(&self) -> Cow<'static, str> {
2582        String::type_name()
2583    }
2584
2585    fn visible(&self, _: &User, _: &SystemVars) -> Result<(), VarError> {
2586        Ok(())
2587    }
2588}
2589
2590impl Var for User {
2591    fn name(&self) -> &'static str {
2592        IS_SUPERUSER_NAME.as_str()
2593    }
2594
2595    fn value(&self) -> String {
2596        self.is_superuser().format()
2597    }
2598
2599    fn description(&self) -> &'static str {
2600        "Reports whether the current session is a superuser (PostgreSQL)."
2601    }
2602
2603    fn type_name(&self) -> Cow<'static, str> {
2604        bool::type_name()
2605    }
2606
2607    fn visible(&self, _: &User, _: &SystemVars) -> Result<(), VarError> {
2608        Ok(())
2609    }
2610}
2611
2612#[cfg(test)]
2613mod isolation_feature_flag_tests {
2614    use super::*;
2615
2616    #[mz_ore::test]
2617    fn gates_strong_session_serializable_value() {
2618        let mut system_vars = SystemVars::new();
2619
2620        // The flag defaults off: the value is rejected regardless of the letter
2621        // case of the variable name. This covers `SET`,
2622        // `SET "TRANSACTION_ISOLATION"`, `ALTER ROLE ... SET`, and connection
2623        // options, which all route through `SessionVars::set` and this shared
2624        // check.
2625        for name in ["transaction_isolation", "TRANSACTION_ISOLATION"] {
2626            let err = check_transaction_isolation_feature_flag(
2627                name,
2628                VarInput::Flat("strong session serializable"),
2629                &system_vars,
2630            )
2631            .expect_err("flag off rejects strong session serializable");
2632            assert!(matches!(err, VarError::RequiresFeatureFlag { .. }));
2633        }
2634
2635        // Ungated levels pass regardless of the flag.
2636        for level in ["serializable", "bounded staleness 5s"] {
2637            check_transaction_isolation_feature_flag(
2638                TRANSACTION_ISOLATION_VAR_NAME,
2639                VarInput::Flat(level),
2640                &system_vars,
2641            )
2642            .expect("ungated level always allowed");
2643        }
2644
2645        // With the flag on, the gated value passes too.
2646        system_vars
2647            .set("enable_session_timelines", VarInput::Flat("on"))
2648            .expect("set flag");
2649        check_transaction_isolation_feature_flag(
2650            TRANSACTION_ISOLATION_VAR_NAME,
2651            VarInput::Flat("strong session serializable"),
2652            &system_vars,
2653        )
2654        .expect("flag on");
2655
2656        // Unrelated variables are ignored, even with a gated-looking value.
2657        check_transaction_isolation_feature_flag(
2658            CLUSTER.name(),
2659            VarInput::Flat("strong session serializable"),
2660            &system_vars,
2661        )
2662        .expect("unrelated var ignored");
2663    }
2664}
2665
2666#[cfg(test)]
2667mod reset_all_tests {
2668    use super::*;
2669    use crate::session::user::SYSTEM_USER;
2670
2671    fn test_vars() -> SessionVars {
2672        SessionVars::new_unchecked(&mz_build_info::DUMMY_BUILD_INFO, SYSTEM_USER.clone(), None)
2673    }
2674
2675    // `reset_all` (used by `DISCARD ALL`) must clear a committed session
2676    // override durably, without depending on a later transaction commit to
2677    // promote the reset. Regression coverage for SQL-529.
2678    #[mz_ore::test]
2679    fn reset_all_clears_committed_session_value() {
2680        let system_vars = SystemVars::new();
2681        let mut vars = test_vars();
2682        let default = vars.application_name().to_string();
2683
2684        // Set non-locally and commit, so the override lives in `session_value`.
2685        vars.set(
2686            &system_vars,
2687            "application_name",
2688            VarInput::Flat("custom"),
2689            false,
2690        )
2691        .expect("set");
2692        vars.end_transaction(EndTransactionAction::Commit);
2693        assert_eq!(vars.application_name(), "custom");
2694        assert_eq!(
2695            vars.inspect("application_name")
2696                .unwrap()
2697                .inspect_session_value()
2698                .map(|v| v.format()),
2699            Some("custom".to_string())
2700        );
2701
2702        vars.reset_all();
2703
2704        // The value falls back to the default, the var is unset, and it will
2705        // not mutate at a later transaction end.
2706        let var = vars.inspect("application_name").unwrap();
2707        assert_eq!(vars.application_name(), default);
2708        assert_eq!(var.inspect_session_value(), None);
2709        assert!(!var.is_mutating());
2710    }
2711
2712    // `reset_all` must fall back to a system/role/startup default installed via
2713    // `set_default`, not the compiled-in default. Guards the "startup/role
2714    // defaults survive DISCARD ALL" contract.
2715    #[mz_ore::test]
2716    fn reset_all_preserves_installed_default() {
2717        let system_vars = SystemVars::new();
2718        let mut vars = test_vars();
2719
2720        vars.set_default("application_name", VarInput::Flat("startup_default"))
2721            .expect("set_default");
2722        vars.set(
2723            &system_vars,
2724            "application_name",
2725            VarInput::Flat("custom"),
2726            false,
2727        )
2728        .expect("set");
2729        vars.end_transaction(EndTransactionAction::Commit);
2730        assert_eq!(vars.application_name(), "custom");
2731
2732        vars.reset_all();
2733
2734        assert_eq!(vars.application_name(), "startup_default");
2735        assert_eq!(
2736            vars.inspect("application_name")
2737                .unwrap()
2738                .inspect_session_value(),
2739            None
2740        );
2741    }
2742}