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("internal error: {0}")]
106    Internal(String),
107}
108
109impl Error {
110    pub fn new(kind: ErrorKind) -> Error {
111        Error { kind }
112    }
113
114    /// Reports additional details about the error, if any are available.
115    pub fn detail(&self) -> Option<String> {
116        match &self.kind {
117            ErrorKind::ReservedSchemaName(_) => {
118                Some("The prefixes \"mz_\" and \"pg_\" are reserved for system schemas.".into())
119            }
120            ErrorKind::ReservedRoleName(_) => {
121                Some("The role \"public\" and the prefixes \"mz_\" and \"pg_\" are reserved for system roles.".into())
122            }
123            ErrorKind::ReservedSystemRoleName(_) => {
124                Some("The role prefixes \"mz_\" and \"pg_\" are reserved for system roles.".into())
125            }
126            ErrorKind::ReservedClusterName(_) => {
127                Some("The prefixes \"mz_\" and \"pg_\" are reserved for system clusters.".into())
128            }
129            ErrorKind::UnstableDependency {unstable_dependencies, ..} => Some(
130                format!(
131                    "The object depends on the following unstable objects:\n    {}",
132                    unstable_dependencies.join("\n    "),
133                )
134            ),
135            ErrorKind::VarError(e) => e.detail(),
136            _ => None,
137        }
138    }
139
140    /// Reports a hint for the user about how the error could be fixed.
141    pub fn hint(&self) -> Option<String> {
142        match &self.kind {
143            ErrorKind::VarError(e) => e.hint(),
144            ErrorKind::InvalidClusterReplicaSize { expected, .. } => Some(format!(
145                "Valid cluster replica sizes are: {}",
146                expected.join(", ")
147            )),
148            _ => None,
149        }
150    }
151}
152
153impl From<SqlCatalogError> for Error {
154    fn from(e: SqlCatalogError) -> Error {
155        Error::new(ErrorKind::from(e))
156    }
157}
158
159impl From<TryFromProtoError> for Error {
160    fn from(e: TryFromProtoError) -> Error {
161        Error::from(crate::durable::CatalogError::from(e))
162    }
163}
164
165impl From<uuid::Error> for Error {
166    fn from(e: uuid::Error) -> Error {
167        Error::new(ErrorKind::from(e))
168    }
169}
170
171impl From<crate::durable::CatalogError> for Error {
172    fn from(e: crate::durable::CatalogError) -> Self {
173        match e {
174            crate::durable::CatalogError::Catalog(e) => Error::new(ErrorKind::from(e)),
175            crate::durable::CatalogError::Durable(e) => Error::new(ErrorKind::from(e)),
176        }
177    }
178}
179
180impl From<VarError> for Error {
181    fn from(e: VarError) -> Self {
182        let kind: ErrorKind = e.into();
183        kind.into()
184    }
185}
186
187#[derive(Debug)]
188pub struct AmbiguousRename {
189    pub depender: String,
190    pub dependee: String,
191    pub message: String,
192}
193
194// Implement `Display` for `MinMax`.
195impl fmt::Display for AmbiguousRename {
196    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
197        if self.depender == self.dependee {
198            write!(
199                f,
200                "renaming conflict: in {}, {}",
201                self.dependee, self.message
202            )
203        } else {
204            write!(
205                f,
206                "renaming conflict: in {}, which uses {}, {}",
207                self.depender, self.dependee, self.message
208            )
209        }
210    }
211}
212
213impl std::error::Error for AmbiguousRename {
214    // Explicitly no source for this kind of error
215    fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
216        None
217    }
218}