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    FailedCatalogMigration {
82        last_seen_version: String,
83        this_version: &'static str,
84        cause: String,
85    },
86    #[error(
87        "cannot migrate builtin schemas from version {last_seen_version} to version {this_version}: {cause}"
88    )]
89    FailedBuiltinSchemaMigration {
90        last_seen_version: String,
91        this_version: String,
92        cause: String,
93    },
94    #[error("failpoint {0} reached)")]
95    FailpointReached(String),
96    #[error("{0}")]
97    Unstructured(String),
98    #[error(transparent)]
99    Durable(#[from] crate::durable::DurableCatalogError),
100    #[error(transparent)]
101    Uuid(#[from] uuid::Error),
102    #[error("role \"{role_name}\" is a member of role \"{member_name}\"")]
103    CircularRoleMembership {
104        role_name: String,
105        member_name: String,
106    },
107    #[error("cluster '{0}' is managed and cannot be directly modified")]
108    ManagedCluster(String),
109    #[error(transparent)]
110    VarError(#[from] VarError),
111    #[error("unknown cluster replica size {size}")]
112    InvalidClusterReplicaSize { size: String, expected: Vec<String> },
113    #[error("failed to get catalog setting: {0}")]
114    SettingError(String),
115    #[error("internal error: {0}")]
116    Internal(String),
117}
118
119impl Error {
120    pub fn new(kind: ErrorKind) -> Error {
121        Error { kind }
122    }
123
124    /// Reports additional details about the error, if any are available.
125    pub fn detail(&self) -> Option<String> {
126        match &self.kind {
127            ErrorKind::ReservedSchemaName(_) => {
128                Some("The prefixes \"mz_\" and \"pg_\" are reserved for system schemas.".into())
129            }
130            ErrorKind::ReservedRoleName(_) => {
131                Some("The role \"public\" and the prefixes \"mz_\" and \"pg_\" are reserved for system roles.".into())
132            }
133            ErrorKind::ReservedSystemRoleName(_) => {
134                Some("The role prefixes \"mz_\" and \"pg_\" are reserved for system roles.".into())
135            }
136            ErrorKind::ReservedClusterName(_) => {
137                Some("The prefixes \"mz_\" and \"pg_\" are reserved for system clusters.".into())
138            }
139            ErrorKind::UnstableDependency {unstable_dependencies, ..} => Some(
140                format!(
141                    "The object depends on the following unstable objects:\n    {}",
142                    unstable_dependencies.join("\n    "),
143                )
144            ),
145            ErrorKind::VarError(e) => e.detail(),
146            _ => None,
147        }
148    }
149
150    /// Reports a hint for the user about how the error could be fixed.
151    pub fn hint(&self) -> Option<String> {
152        match &self.kind {
153            ErrorKind::VarError(e) => e.hint(),
154            ErrorKind::InvalidClusterReplicaSize { expected, .. } => Some(format!(
155                "Valid cluster replica sizes are: {}",
156                expected.join(", ")
157            )),
158            _ => None,
159        }
160    }
161}
162
163impl From<SqlCatalogError> for Error {
164    fn from(e: SqlCatalogError) -> Error {
165        Error::new(ErrorKind::from(e))
166    }
167}
168
169impl From<TryFromProtoError> for Error {
170    fn from(e: TryFromProtoError) -> Error {
171        Error::from(crate::durable::CatalogError::from(e))
172    }
173}
174
175impl From<uuid::Error> for Error {
176    fn from(e: uuid::Error) -> Error {
177        Error::new(ErrorKind::from(e))
178    }
179}
180
181impl From<crate::durable::CatalogError> for Error {
182    fn from(e: crate::durable::CatalogError) -> Self {
183        match e {
184            crate::durable::CatalogError::Catalog(e) => Error::new(ErrorKind::from(e)),
185            crate::durable::CatalogError::Durable(e) => Error::new(ErrorKind::from(e)),
186        }
187    }
188}
189
190impl From<VarError> for Error {
191    fn from(e: VarError) -> Self {
192        let kind: ErrorKind = e.into();
193        kind.into()
194    }
195}
196
197#[derive(Debug)]
198pub struct AmbiguousRename {
199    pub depender: String,
200    pub dependee: String,
201    pub message: String,
202}
203
204// Implement `Display` for `MinMax`.
205impl fmt::Display for AmbiguousRename {
206    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
207        if self.depender == self.dependee {
208            write!(
209                f,
210                "renaming conflict: in {}, {}",
211                self.dependee, self.message
212            )
213        } else {
214            write!(
215                f,
216                "renaming conflict: in {}, which uses {}, {}",
217                self.depender, self.dependee, self.message
218            )
219        }
220    }
221}
222
223impl std::error::Error for AmbiguousRename {
224    // Explicitly no source for this kind of error
225    fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
226        None
227    }
228}