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.
910use std::borrow::Cow;
1112use itertools::Itertools;
1314use mz_ore::str::StrExt;
1516use crate::session::vars::{FeatureFlag, Var};
1718/// 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 )]
29ConstrainedParameter {
30/// Name of the parameter.
31name: &'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 )]
44FixedValueParameter {
45/// Name of the parameter.
46name: &'static str,
47/// The value the parameter is fixed at.
48value: 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 )]
56InvalidParameterType {
57/// Name of the parameter.
58name: &'static str,
59/// Required type of the parameter.
60required_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 )]
73InvalidParameterValue {
74/// Name of the parameter.
75name: &'static str,
76/// Invalid values.
77invalid_values: Vec<String>,
78/// Reason the values are invalid.
79reason: String,
80 },
81/// The specified session parameter is read only.
82#[error("parameter {} cannot be changed", .0.quoted())]
83ReadOnlyParameter(&'static str),
84/// The named parameter is unknown to the system.
85#[error("unrecognized configuration parameter {}", .0.quoted())]
86UnknownParameter(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())]
89RequiresUnsafeMode(&'static str),
90#[error(
91"{} is not {}",
92 .feature_flag.feature_desc,
93if .feature_flag.flag.is_unsafe() { "supported" } else { "available" }
94 )]
95RequiresFeatureFlag { feature_flag: &'static FeatureFlag },
96}
9798impl VarError {
99pub fn detail(&self) -> Option<String> {
100match self {
101Self::RequiresFeatureFlag { feature_flag } => {
102if feature_flag.flag.is_unsafe() {
103Some(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 {
108Some(format!(
109"The requested feature ({}) is in private preview.",
110 feature_flag.flag.name(),
111 ))
112 }
113 }
114_ => None,
115 }
116 }
117118pub fn hint(&self) -> Option<String> {
119match 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() => {
125Some(
126"Contact support to discuss enabling the feature in your Materialize region."
127.into(),
128 )
129 }
130_ => None,
131 }
132 }
133}
134135/// 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`].
146ConstrainedParameter {
147 invalid_values: Vec<String>,
148 valid_values: Option<Vec<&'static str>>,
149 },
150/// Minimal version of [`VarError::FixedValueParameter`].
151FixedValueParameter,
152/// Minimal version of [`VarError::InvalidParameterType`].
153InvalidParameterType,
154/// Minimal version of [`VarError::InvalidParameterValue`].
155InvalidParameterValue {
156 invalid_values: Vec<String>,
157 reason: String,
158 },
159}
160161impl VarParseError {
162/// Enrich this [`VarParseError`] with information about the [`Var`] we parsed.
163pub fn into_var_error(self, var: &dyn Var) -> VarError {
164match 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}