1#[cfg_attr(
14 feature = "wire",
15 derive(candid::CandidType, serde::Deserialize, serde::Serialize)
16)]
17#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
18pub enum DiagnosticCode {
19 QueryValidate,
20 QueryIntent,
21 QueryPlan,
22 QueryAccessRequirement,
23 QueryUnorderedPagination,
24 QueryInvalidContinuationCursor,
25 QueryNotFound,
26 QueryNotUnique,
27 QueryNumericOverflow,
28 QueryNumericNotRepresentable,
29 QueryUnsupportedSqlFeature,
30 QuerySqlSurfaceMismatch,
31 SchemaDdlAdmission,
32 StoreNotFound,
33 StoreCorruption,
34 StoreInvariantViolation,
35 RuntimeCorruption,
36 RuntimeIncompatiblePersistedFormat,
37 RuntimeInvariantViolation,
38 RuntimeConflict,
39 RuntimeNotFound,
40 RuntimeUnsupported,
41 RuntimeInternal,
42}
43
44impl DiagnosticCode {
45 #[must_use]
47 pub const fn class(self) -> ErrorClass {
48 match self {
49 Self::StoreCorruption | Self::RuntimeCorruption => ErrorClass::Corruption,
50 Self::RuntimeIncompatiblePersistedFormat => ErrorClass::IncompatiblePersistedFormat,
51 Self::QueryNotFound | Self::StoreNotFound | Self::RuntimeNotFound => {
52 ErrorClass::NotFound
53 }
54 Self::RuntimeConflict => ErrorClass::Conflict,
55 Self::QueryUnsupportedSqlFeature
56 | Self::QuerySqlSurfaceMismatch
57 | Self::RuntimeUnsupported => ErrorClass::Unsupported,
58 Self::StoreInvariantViolation | Self::RuntimeInvariantViolation => {
59 ErrorClass::InvariantViolation
60 }
61 Self::RuntimeInternal => ErrorClass::Internal,
62 Self::QueryValidate
63 | Self::QueryIntent
64 | Self::QueryPlan
65 | Self::QueryAccessRequirement
66 | Self::QueryUnorderedPagination
67 | Self::QueryInvalidContinuationCursor
68 | Self::QueryNotUnique
69 | Self::QueryNumericOverflow
70 | Self::QueryNumericNotRepresentable
71 | Self::SchemaDdlAdmission => ErrorClass::Query,
72 }
73 }
74
75 #[must_use]
77 pub const fn origin(self) -> ErrorOrigin {
78 match self {
79 Self::StoreNotFound | Self::StoreCorruption | Self::StoreInvariantViolation => {
80 ErrorOrigin::Store
81 }
82 Self::RuntimeCorruption
83 | Self::RuntimeIncompatiblePersistedFormat
84 | Self::RuntimeInvariantViolation
85 | Self::RuntimeConflict
86 | Self::RuntimeNotFound
87 | Self::RuntimeUnsupported
88 | Self::RuntimeInternal => ErrorOrigin::Runtime,
89 Self::QueryValidate
90 | Self::QueryIntent
91 | Self::QueryPlan
92 | Self::QueryAccessRequirement
93 | Self::QueryUnorderedPagination
94 | Self::QueryInvalidContinuationCursor
95 | Self::QueryNotFound
96 | Self::QueryNotUnique
97 | Self::QueryNumericOverflow
98 | Self::QueryNumericNotRepresentable
99 | Self::QueryUnsupportedSqlFeature
100 | Self::QuerySqlSurfaceMismatch
101 | Self::SchemaDdlAdmission => ErrorOrigin::Query,
102 }
103 }
104}
105
106#[cfg_attr(
113 feature = "wire",
114 derive(candid::CandidType, serde::Deserialize, serde::Serialize)
115)]
116#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
117pub enum ErrorClass {
118 Query,
119 Corruption,
120 IncompatiblePersistedFormat,
121 NotFound,
122 Internal,
123 Conflict,
124 Unsupported,
125 InvariantViolation,
126}
127
128#[cfg_attr(
135 feature = "wire",
136 derive(candid::CandidType, serde::Deserialize, serde::Serialize)
137)]
138#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
139pub enum ErrorOrigin {
140 Cursor,
141 Executor,
142 Identity,
143 Index,
144 Interface,
145 Planner,
146 Query,
147 Recovery,
148 Response,
149 Runtime,
150 Serialize,
151 Store,
152}
153
154#[cfg_attr(
161 feature = "wire",
162 derive(candid::CandidType, serde::Deserialize, serde::Serialize)
163)]
164#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
165pub enum QueryErrorKind {
166 Validate,
167 Intent,
168 Plan,
169 AccessRequirement,
170 UnorderedPagination,
171 InvalidContinuationCursor,
172 NotFound,
173 NotUnique,
174}
175
176#[cfg_attr(
183 feature = "wire",
184 derive(candid::CandidType, serde::Deserialize, serde::Serialize)
185)]
186#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
187pub enum RuntimeErrorKind {
188 Corruption,
189 IncompatiblePersistedFormat,
190 InvariantViolation,
191 Conflict,
192 NotFound,
193 Unsupported,
194 Internal,
195}
196
197#[cfg_attr(
204 feature = "wire",
205 derive(candid::CandidType, serde::Deserialize, serde::Serialize)
206)]
207#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
208pub enum SqlFeatureCode {
209 AggregateFilterClause,
210 AlterStatementBeyondAlterTable,
211 AlterTableAddColumnDuplicateDefault,
212 AlterTableAddColumnModifiers,
213 AlterTableAddStatementBeyondAddColumn,
214 AlterTableAlterColumnDropUnsupportedAction,
215 AlterTableAlterColumnModifiers,
216 AlterTableAlterColumnSetUnsupportedAction,
217 AlterTableAlterColumnUnsupportedAction,
218 AlterTableAlterStatementBeyondAlterColumn,
219 AlterTableDropColumnIfExistsSyntax,
220 AlterTableDropColumnModifiers,
221 AlterTableDropStatementBeyondDropColumn,
222 AlterTableRenameColumnMissingTo,
223 AlterTableRenameColumnModifiers,
224 AlterTableRenameStatementBeyondRenameColumn,
225 AlterTableUnsupportedOperation,
226 ColumnAlias,
227 CreateIndexIfNotExistsSyntax,
228 CreateIndexKeyOrderingModifiers,
229 CreateIndexModifiers,
230 CreateStatementBeyondCreateIndex,
231 DescribeModifier,
232 DdlSchemaVersionDuplicateExpectedClause,
233 DdlSchemaVersionDuplicateSetClause,
234 DropIndexModifiers,
235 DropIndexIfExistsSyntax,
236 DropStatementBeyondDropIndex,
237 ExpressionIndexUnsupportedFunction,
238 Having,
239 Insert,
240 Join,
241 LikePatternBeyondTrailingPrefix,
242 LowerFieldPredicateUnsupported,
243 MultiStatementSql,
244 NestedAggregateInput,
245 NestedProjectionFunctionInArithmetic,
246 OrderByUnsupportedForm,
247 Other,
248 ParameterBinding,
249 ParameterizedSchemaVersion,
250 PredicateStartsWithFirstArgument,
251 QuotedIdentifiers,
252 ReturningUnsupportedShape,
253 ScalarFunctionExpressionPosition,
254 ScaleTakingNumericFunctionExpressionPosition,
255 SearchedCaseGroupedOrderBy,
256 ShowColumnsModifiers,
257 ShowEntitiesModifiers,
258 ShowIndexesModifiers,
259 ShowMemoryModifiers,
260 ShowStoresModifiers,
261 ShowUnsupportedCommand,
262 SimpleCaseExpression,
263 StandaloneLiteralProjectionItem,
264 SupportedGroupedOrderByExpressionFamily,
265 SupportedOrderByExpressionFamily,
266 UnionIntersectExcept,
267 UnsupportedFunctionNamespace,
268 Update,
269 UpperFieldPredicateUnsupported,
270 WindowFunction,
271 With,
272}
273
274#[cfg_attr(
281 feature = "wire",
282 derive(candid::CandidType, serde::Deserialize, serde::Serialize)
283)]
284#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
285pub enum SqlSurfaceMismatchCode {
286 QueryRejectsInsert,
287 QueryRejectsUpdate,
288 QueryRejectsDelete,
289 UpdateRejectsSelect,
290 UpdateRejectsExplain,
291 UpdateRejectsDescribe,
292 UpdateRejectsShowIndexes,
293 UpdateRejectsShowColumns,
294 UpdateRejectsShowEntities,
295 UpdateRejectsShowStores,
296 UpdateRejectsShowMemory,
297}
298
299#[cfg_attr(
306 feature = "wire",
307 derive(candid::CandidType, serde::Deserialize, serde::Serialize)
308)]
309#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
310pub enum SchemaDdlAdmissionCode {
311 MissingExpectedSchemaVersion,
312 MissingNextSchemaVersion,
313 StaleExpectedSchemaVersion,
314 InvalidExpectedSchemaVersion,
315 InvalidNextSchemaVersion,
316 AcceptedSchemaChangeWithoutVersionBump,
317 EmptyVersionBump,
318 VersionGap,
319 VersionRollback,
320 FingerprintMethodMismatch,
321 UnsupportedTransitionClass,
322 PhysicalRunnerMissing,
323 ValidationFailed,
324 PublicationRaceLost,
325}
326
327#[cfg_attr(
334 feature = "wire",
335 derive(candid::CandidType, serde::Deserialize, serde::Serialize)
336)]
337#[derive(Clone, Debug, Eq, PartialEq)]
338pub enum DiagnosticDetail {
339 QueryKind { kind: QueryErrorKind },
340 RuntimeKind { kind: RuntimeErrorKind },
341 SchemaDdlAdmission { reason: SchemaDdlAdmissionCode },
342 UnsupportedSqlFeature { feature: SqlFeatureCode },
343 SqlSurfaceMismatch { mismatch: SqlSurfaceMismatchCode },
344}
345
346#[cfg_attr(
353 feature = "wire",
354 derive(candid::CandidType, serde::Deserialize, serde::Serialize)
355)]
356#[derive(Clone, Debug, Eq, PartialEq)]
357pub struct Diagnostic {
358 code: DiagnosticCode,
359 origin: ErrorOrigin,
360 detail: Option<DiagnosticDetail>,
361}
362
363impl Diagnostic {
364 #[must_use]
366 pub const fn new(
367 code: DiagnosticCode,
368 origin: ErrorOrigin,
369 detail: Option<DiagnosticDetail>,
370 ) -> Self {
371 Self {
372 code,
373 origin,
374 detail,
375 }
376 }
377
378 #[must_use]
380 pub const fn from_code(code: DiagnosticCode) -> Self {
381 Self::new(code, code.origin(), None)
382 }
383
384 #[must_use]
386 pub const fn code(&self) -> DiagnosticCode {
387 self.code
388 }
389
390 #[must_use]
392 pub const fn class(&self) -> ErrorClass {
393 self.code.class()
394 }
395
396 #[must_use]
398 pub const fn origin(&self) -> ErrorOrigin {
399 self.origin
400 }
401
402 #[must_use]
404 pub const fn detail(&self) -> Option<&DiagnosticDetail> {
405 self.detail.as_ref()
406 }
407}
408
409#[cfg(test)]
410mod tests {
411 use super::{Diagnostic, DiagnosticCode, ErrorClass, ErrorOrigin};
412
413 #[test]
414 fn diagnostic_from_code_uses_default_origin() {
415 let diagnostic = Diagnostic::from_code(DiagnosticCode::QueryPlan);
416
417 assert_eq!(diagnostic.code(), DiagnosticCode::QueryPlan);
418 assert_eq!(diagnostic.origin(), ErrorOrigin::Query);
419 }
420
421 #[test]
422 fn diagnostic_code_reports_broad_class() {
423 assert_eq!(
424 DiagnosticCode::QueryUnsupportedSqlFeature.class(),
425 ErrorClass::Unsupported
426 );
427 assert_eq!(
428 DiagnosticCode::QuerySqlSurfaceMismatch.class(),
429 ErrorClass::Unsupported
430 );
431 assert_eq!(DiagnosticCode::QueryPlan.class(), ErrorClass::Query);
432 assert_eq!(
433 DiagnosticCode::StoreCorruption.class(),
434 ErrorClass::Corruption
435 );
436 }
437}