1use 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 #[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 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 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
204impl 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 fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
226 None
227 }
228}