mz_sql/session/vars/
errors.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
10use std::borrow::Cow;
11
12use itertools::Itertools;
13
14use mz_ore::str::StrExt;
15
16use crate::session::vars::{FeatureFlag, Var};
17
18/// Errors that can occur when working with [`Var`]s
19///
20/// [`Var`]: crate::session::vars::Var
21#[derive(Clone, Debug, Eq, PartialEq, thiserror::Error)]
22pub enum VarError {
23    /// The specified session parameter is constrained to a finite set of values.
24    #[error(
25        "invalid value for parameter {}: {}",
26        name.quoted(),
27        invalid_values.iter().map(|v| v.quoted()).join(",")
28    )]
29    ConstrainedParameter {
30        /// Name of the parameter.
31        name: &'static str,
32        invalid_values: Vec<String>,
33        valid_values: Option<Vec<&'static str>>,
34    },
35    /// The specified parameter is fixed to a single specific value.
36    ///
37    /// We allow setting the parameter to its fixed value for compatibility
38    /// with PostgreSQL-based tools.
39    #[error(
40        "parameter {} can only be set to {}",
41        name.quoted(),
42        value.quoted(),
43    )]
44    FixedValueParameter {
45        /// Name of the parameter.
46        name: &'static str,
47        /// The value the parameter is fixed at.
48        value: String,
49    },
50    /// The value for the specified parameter does not have the right type.
51    #[error(
52        "parameter {} requires a {} value",
53        name.quoted(),
54        required_type.quoted()
55    )]
56    InvalidParameterType {
57        /// Name of the parameter.
58        name: &'static str,
59        /// Required type of the parameter.
60        required_type: Cow<'static, str>,
61    },
62    /// The value of the specified parameter is incorrect.
63    #[error(
64        "parameter {} cannot have value {}: {}",
65        name.quoted(),
66        invalid_values
67            .iter()
68            .map(|v| v.quoted().to_string())
69            .collect::<Vec<_>>()
70            .join(","),
71        reason,
72    )]
73    InvalidParameterValue {
74        /// Name of the parameter.
75        name: &'static str,
76        /// Invalid values.
77        invalid_values: Vec<String>,
78        /// Reason the values are invalid.
79        reason: String,
80    },
81    /// The specified session parameter is read only.
82    #[error("parameter {} cannot be changed", .0.quoted())]
83    ReadOnlyParameter(&'static str),
84    /// The named parameter is unknown to the system.
85    #[error("unrecognized configuration parameter {}", .0.quoted())]
86    UnknownParameter(String),
87    /// The specified session parameter is read only unless in unsafe mode.
88    #[error("parameter {} can only be set in unsafe mode", .0.quoted())]
89    RequiresUnsafeMode(&'static str),
90    #[error(
91        "{} is not {}",
92        .feature_flag.feature_desc,
93        if .feature_flag.flag.is_unsafe() { "supported" } else { "available" }
94    )]
95    RequiresFeatureFlag { feature_flag: &'static FeatureFlag },
96}
97
98impl VarError {
99    pub fn detail(&self) -> Option<String> {
100        match self {
101            Self::RequiresFeatureFlag { feature_flag } => {
102                if feature_flag.flag.is_unsafe() {
103                    Some(format!(
104                        "The requested feature ({}) is unsafe and is meant only for internal development and testing of Materialize.",
105                        feature_flag.flag.name(),
106                    ))
107                } else {
108                    Some(format!(
109                        "The requested feature ({}) is in private preview.",
110                        feature_flag.flag.name(),
111                    ))
112                }
113            }
114            _ => None,
115        }
116    }
117
118    pub fn hint(&self) -> Option<String> {
119        match self {
120            VarError::ConstrainedParameter {
121                valid_values: Some(valid_values),
122                ..
123            } => Some(format!("Available values: {}.", valid_values.join(", "))),
124            VarError::RequiresFeatureFlag { feature_flag } if !feature_flag.flag.is_unsafe() => {
125                Some(
126                    "Contact support to discuss enabling the feature in your Materialize region."
127                        .into(),
128                )
129            }
130            _ => None,
131        }
132    }
133}
134
135/// Errors that can occur when parsing [`VarInput`].
136///
137/// Note: This exists as a separate type from [`VarError`] because [`VarError`] wants to know about
138/// the [`Var`] we're parsing. We could provide this info to [`Value::parse`] but it's simpler to
139/// later enrich with [`VarParseError::into_var_error`].
140///
141/// [`VarInput`]: crate::session::vars::VarInput
142/// [`Value::parse`]: crate::session::vars::value::Value::parse
143#[derive(Debug)]
144pub enum VarParseError {
145    /// Minimal version of [`VarError::ConstrainedParameter`].
146    ConstrainedParameter {
147        invalid_values: Vec<String>,
148        valid_values: Option<Vec<&'static str>>,
149    },
150    /// Minimal version of [`VarError::FixedValueParameter`].
151    FixedValueParameter,
152    /// Minimal version of [`VarError::InvalidParameterType`].
153    InvalidParameterType,
154    /// Minimal version of [`VarError::InvalidParameterValue`].
155    InvalidParameterValue {
156        invalid_values: Vec<String>,
157        reason: String,
158    },
159}
160
161impl VarParseError {
162    /// Enrich this [`VarParseError`] with information about the [`Var`] we parsed.
163    pub fn into_var_error(self, var: &dyn Var) -> VarError {
164        match self {
165            VarParseError::ConstrainedParameter {
166                invalid_values,
167                valid_values,
168            } => VarError::ConstrainedParameter {
169                name: var.name(),
170                invalid_values,
171                valid_values,
172            },
173            VarParseError::FixedValueParameter => VarError::FixedValueParameter {
174                name: var.name(),
175                value: var.value(),
176            },
177            VarParseError::InvalidParameterType => VarError::InvalidParameterType {
178                name: var.name(),
179                required_type: var.type_name(),
180            },
181            VarParseError::InvalidParameterValue {
182                invalid_values,
183                reason,
184            } => VarError::InvalidParameterValue {
185                name: var.name(),
186                invalid_values,
187                reason,
188            },
189        }
190    }
191}