mz_catalog/memory/
error.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::fmt;
11
12use mz_ore::str::StrExt;
13use mz_proto::TryFromProtoError;
14use mz_sql::catalog::CatalogError as SqlCatalogError;
15use mz_sql::session::vars::VarError;
16
17#[derive(Debug, thiserror::Error)]
18#[error(transparent)]
19pub struct Error {
20    #[from]
21    pub kind: ErrorKind,
22}
23
24#[derive(Debug, thiserror::Error)]
25pub enum ErrorKind {
26    #[error("corrupt catalog: {detail}")]
27    Corruption { detail: String },
28    #[error("oid counter overflows i64")]
29    OidExhaustion,
30    #[error(transparent)]
31    Sql(#[from] SqlCatalogError),
32    #[error("unacceptable schema name '{0}'")]
33    ReservedSchemaName(String),
34    #[error("role name {} is reserved", .0.quoted())]
35    ReservedRoleName(String),
36    #[error("role name {} is reserved", .0.quoted())]
37    ReservedSystemRoleName(String),
38    #[error("role name {} cannot be granted", .0.quoted())]
39    UngrantableRoleName(String),
40    #[error("cluster name {} is reserved", .0.quoted())]
41    ReservedClusterName(String),
42    #[error("network policy name {} is reserved", .0.quoted())]
43    ReservedNetworkPolicyName(String),
44    #[error("system network policy '{0}' cannot be modified")]
45    ReadOnlyNetworkPolicy(String),
46    #[error("replica name {} is reserved", .0.quoted())]
47    ReservedReplicaName(String),
48    #[error("system cluster '{0}' cannot be modified")]
49    ReadOnlyCluster(String),
50    #[error("system cluster replica '{0}' cannot be modified")]
51    ReadOnlyClusterReplica(String),
52    #[error("system database '{0}' cannot be modified")]
53    ReadOnlyDatabase(String),
54    #[error("system schema '{0}' cannot be modified")]
55    ReadOnlySystemSchema(String),
56    #[error("system item '{0}' cannot be modified")]
57    ReadOnlyItem(String),
58    #[error("cannot drop non-empty schema '{0}'")]
59    SchemaNotEmpty(String),
60    #[error("non-temporary items cannot depend on temporary item '{0}'")]
61    InvalidTemporaryDependency(String),
62    #[error("cannot create temporary item in non-temporary schema")]
63    InvalidTemporarySchema,
64    #[error("catalog item '{depender_name}' depends on system logging, but logging is disabled")]
65    UnsatisfiableLoggingDependency { depender_name: String },
66    /// Attempted to create an object that has unstable dependencies.
67    #[error("cannot create {object_type} with unstable dependencies")]
68    UnstableDependency {
69        object_type: String,
70        unstable_dependencies: Vec<String>,
71    },
72    #[error(transparent)]
73    AmbiguousRename(#[from] AmbiguousRename),
74    #[error("cannot rename type: {0}")]
75    TypeRename(String),
76    #[error("cannot rename schemas in the ambient database: {}", .0.quoted())]
77    AmbientSchemaRename(String),
78    #[error(
79        "cannot migrate from catalog version {last_seen_version} to version {this_version} (earlier versions might still work): {cause}"
80    )]
81    FailedMigration {
82        last_seen_version: String,
83        this_version: &'static str,
84        cause: String,
85    },
86    #[error("failpoint {0} reached)")]
87    FailpointReached(String),
88    #[error("{0}")]
89    Unstructured(String),
90    #[error(transparent)]
91    Durable(#[from] crate::durable::DurableCatalogError),
92    #[error(transparent)]
93    Uuid(#[from] uuid::Error),
94    #[error("role \"{role_name}\" is a member of role \"{member_name}\"")]
95    CircularRoleMembership {
96        role_name: String,
97        member_name: String,
98    },
99    #[error("cluster '{0}' is managed and cannot be directly modified")]
100    ManagedCluster(String),
101    #[error(transparent)]
102    VarError(#[from] VarError),
103    #[error("unknown cluster replica size {size}")]
104    InvalidClusterReplicaSize { size: String, expected: Vec<String> },
105    #[error("failed to get catalog setting: {0}")]
106    SettingError(String),
107    #[error("internal error: {0}")]
108    Internal(String),
109}
110
111impl Error {
112    pub fn new(kind: ErrorKind) -> Error {
113        Error { kind }
114    }
115
116    /// Reports additional details about the error, if any are available.
117    pub fn detail(&self) -> Option<String> {
118        match &self.kind {
119            ErrorKind::ReservedSchemaName(_) => {
120                Some("The prefixes \"mz_\" and \"pg_\" are reserved for system schemas.".into())
121            }
122            ErrorKind::ReservedRoleName(_) => {
123                Some("The role \"public\" and the prefixes \"mz_\" and \"pg_\" are reserved for system roles.".into())
124            }
125            ErrorKind::ReservedSystemRoleName(_) => {
126                Some("The role prefixes \"mz_\" and \"pg_\" are reserved for system roles.".into())
127            }
128            ErrorKind::ReservedClusterName(_) => {
129                Some("The prefixes \"mz_\" and \"pg_\" are reserved for system clusters.".into())
130            }
131            ErrorKind::UnstableDependency {unstable_dependencies, ..} => Some(
132                format!(
133                    "The object depends on the following unstable objects:\n    {}",
134                    unstable_dependencies.join("\n    "),
135                )
136            ),
137            ErrorKind::VarError(e) => e.detail(),
138            _ => None,
139        }
140    }
141
142    /// Reports a hint for the user about how the error could be fixed.
143    pub fn hint(&self) -> Option<String> {
144        match &self.kind {
145            ErrorKind::VarError(e) => e.hint(),
146            ErrorKind::InvalidClusterReplicaSize { expected, .. } => Some(format!(
147                "Valid cluster replica sizes are: {}",
148                expected.join(", ")
149            )),
150            _ => None,
151        }
152    }
153}
154
155impl From<SqlCatalogError> for Error {
156    fn from(e: SqlCatalogError) -> Error {
157        Error::new(ErrorKind::from(e))
158    }
159}
160
161impl From<TryFromProtoError> for Error {
162    fn from(e: TryFromProtoError) -> Error {
163        Error::from(crate::durable::CatalogError::from(e))
164    }
165}
166
167impl From<uuid::Error> for Error {
168    fn from(e: uuid::Error) -> Error {
169        Error::new(ErrorKind::from(e))
170    }
171}
172
173impl From<crate::durable::CatalogError> for Error {
174    fn from(e: crate::durable::CatalogError) -> Self {
175        match e {
176            crate::durable::CatalogError::Catalog(e) => Error::new(ErrorKind::from(e)),
177            crate::durable::CatalogError::Durable(e) => Error::new(ErrorKind::from(e)),
178        }
179    }
180}
181
182impl From<VarError> for Error {
183    fn from(e: VarError) -> Self {
184        let kind: ErrorKind = e.into();
185        kind.into()
186    }
187}
188
189#[derive(Debug)]
190pub struct AmbiguousRename {
191    pub depender: String,
192    pub dependee: String,
193    pub message: String,
194}
195
196// Implement `Display` for `MinMax`.
197impl fmt::Display for AmbiguousRename {
198    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
199        if self.depender == self.dependee {
200            write!(
201                f,
202                "renaming conflict: in {}, {}",
203                self.dependee, self.message
204            )
205        } else {
206            write!(
207                f,
208                "renaming conflict: in {}, which uses {}, {}",
209                self.depender, self.dependee, self.message
210            )
211        }
212    }
213}
214
215impl std::error::Error for AmbiguousRename {
216    // Explicitly no source for this kind of error
217    fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
218        None
219    }
220}