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 RuntimeBoundaryCode {
209 SqlSurfaceControllerRequired,
210 SchemaSurfaceControllerRequired,
211 SqlQueryNoConfiguredEntities,
212 SqlQueryEntityNotConfigured,
213 SqlDdlTargetRequired,
214 SqlDdlEntityNotConfigured,
215 QueryResponseRowsRequired,
216 QueryResponseGroupedRowsRequired,
217 MutationResultEntityRequired,
218 MutationResultEntitiesRequired,
219 MutationResultIdRequired,
220 MutationResultIdsRequired,
221 RowProjectionFieldNotConfigured,
222}
223
224#[cfg_attr(
231 feature = "wire",
232 derive(candid::CandidType, serde::Deserialize, serde::Serialize)
233)]
234#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
235pub enum SqlFeatureCode {
236 AggregateFilterClause,
237 AlterStatementBeyondAlterTable,
238 AlterTableAddColumnDuplicateDefault,
239 AlterTableAddColumnModifiers,
240 AlterTableAddStatementBeyondAddColumn,
241 AlterTableAlterColumnDropUnsupportedAction,
242 AlterTableAlterColumnModifiers,
243 AlterTableAlterColumnSetUnsupportedAction,
244 AlterTableAlterColumnUnsupportedAction,
245 AlterTableAlterStatementBeyondAlterColumn,
246 AlterTableDropColumnIfExistsSyntax,
247 AlterTableDropColumnModifiers,
248 AlterTableDropStatementBeyondDropColumn,
249 AlterTableRenameColumnMissingTo,
250 AlterTableRenameColumnModifiers,
251 AlterTableRenameStatementBeyondRenameColumn,
252 AlterTableUnsupportedOperation,
253 ColumnAlias,
254 CreateIndexIfNotExistsSyntax,
255 CreateIndexKeyOrderingModifiers,
256 CreateIndexModifiers,
257 CreateStatementBeyondCreateIndex,
258 DescribeModifier,
259 DdlSchemaVersionDuplicateExpectedClause,
260 DdlSchemaVersionDuplicateSetClause,
261 DropIndexModifiers,
262 DropIndexIfExistsSyntax,
263 DropStatementBeyondDropIndex,
264 ExpressionIndexUnsupportedFunction,
265 Having,
266 Insert,
267 Join,
268 LikePatternBeyondTrailingPrefix,
269 LowerFieldPredicateUnsupported,
270 MultiStatementSql,
271 NestedAggregateInput,
272 NestedProjectionFunctionInArithmetic,
273 OrderByUnsupportedForm,
274 Other,
275 ParameterBinding,
276 ParameterizedSchemaVersion,
277 PredicateStartsWithFirstArgument,
278 QuotedIdentifiers,
279 ReturningUnsupportedShape,
280 ScalarFunctionExpressionPosition,
281 ScaleTakingNumericFunctionExpressionPosition,
282 SearchedCaseGroupedOrderBy,
283 ShowColumnsModifiers,
284 ShowEntitiesModifiers,
285 ShowIndexesModifiers,
286 ShowMemoryModifiers,
287 ShowStoresModifiers,
288 ShowUnsupportedCommand,
289 SimpleCaseExpression,
290 StandaloneLiteralProjectionItem,
291 SupportedGroupedOrderByExpressionFamily,
292 SupportedOrderByExpressionFamily,
293 UnionIntersectExcept,
294 UnsupportedFunctionNamespace,
295 Update,
296 UpperFieldPredicateUnsupported,
297 WindowFunction,
298 With,
299}
300
301#[cfg_attr(
308 feature = "wire",
309 derive(candid::CandidType, serde::Deserialize, serde::Serialize)
310)]
311#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
312pub enum SqlSurfaceMismatchCode {
313 QueryRejectsInsert,
314 QueryRejectsUpdate,
315 QueryRejectsDelete,
316 UpdateRejectsSelect,
317 UpdateRejectsExplain,
318 UpdateRejectsDescribe,
319 UpdateRejectsShowIndexes,
320 UpdateRejectsShowColumns,
321 UpdateRejectsShowEntities,
322 UpdateRejectsShowStores,
323 UpdateRejectsShowMemory,
324}
325
326#[cfg_attr(
333 feature = "wire",
334 derive(candid::CandidType, serde::Deserialize, serde::Serialize)
335)]
336#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
337pub enum SchemaDdlAdmissionCode {
338 MissingExpectedSchemaVersion,
339 MissingNextSchemaVersion,
340 StaleExpectedSchemaVersion,
341 InvalidExpectedSchemaVersion,
342 InvalidNextSchemaVersion,
343 AcceptedSchemaChangeWithoutVersionBump,
344 EmptyVersionBump,
345 VersionGap,
346 VersionRollback,
347 FingerprintMethodMismatch,
348 UnsupportedTransitionClass,
349 PhysicalRunnerMissing,
350 ValidationFailed,
351 PublicationRaceLost,
352}
353
354#[cfg_attr(
361 feature = "wire",
362 derive(candid::CandidType, serde::Deserialize, serde::Serialize)
363)]
364#[derive(Clone, Debug, Eq, PartialEq)]
365pub enum DiagnosticDetail {
366 QueryKind { kind: QueryErrorKind },
367 RuntimeKind { kind: RuntimeErrorKind },
368 RuntimeBoundary { boundary: RuntimeBoundaryCode },
369 SchemaDdlAdmission { reason: SchemaDdlAdmissionCode },
370 UnsupportedSqlFeature { feature: SqlFeatureCode },
371 SqlSurfaceMismatch { mismatch: SqlSurfaceMismatchCode },
372}
373
374#[cfg_attr(
381 feature = "wire",
382 derive(candid::CandidType, serde::Deserialize, serde::Serialize)
383)]
384#[derive(Clone, Debug, Eq, PartialEq)]
385pub struct Diagnostic {
386 code: DiagnosticCode,
387 origin: ErrorOrigin,
388 detail: Option<DiagnosticDetail>,
389}
390
391impl Diagnostic {
392 #[must_use]
394 pub const fn new(
395 code: DiagnosticCode,
396 origin: ErrorOrigin,
397 detail: Option<DiagnosticDetail>,
398 ) -> Self {
399 Self {
400 code,
401 origin,
402 detail,
403 }
404 }
405
406 #[must_use]
408 pub const fn from_code(code: DiagnosticCode) -> Self {
409 Self::new(code, code.origin(), None)
410 }
411
412 #[must_use]
414 pub const fn code(&self) -> DiagnosticCode {
415 self.code
416 }
417
418 #[must_use]
420 pub const fn class(&self) -> ErrorClass {
421 self.code.class()
422 }
423
424 #[must_use]
426 pub const fn origin(&self) -> ErrorOrigin {
427 self.origin
428 }
429
430 #[must_use]
432 pub const fn detail(&self) -> Option<&DiagnosticDetail> {
433 self.detail.as_ref()
434 }
435}
436
437#[cfg(test)]
438mod tests {
439 use super::{Diagnostic, DiagnosticCode, ErrorClass, ErrorOrigin};
440
441 #[test]
442 fn diagnostic_from_code_uses_default_origin() {
443 let diagnostic = Diagnostic::from_code(DiagnosticCode::QueryPlan);
444
445 assert_eq!(diagnostic.code(), DiagnosticCode::QueryPlan);
446 assert_eq!(diagnostic.origin(), ErrorOrigin::Query);
447 }
448
449 #[test]
450 fn diagnostic_code_reports_broad_class() {
451 assert_eq!(
452 DiagnosticCode::QueryUnsupportedSqlFeature.class(),
453 ErrorClass::Unsupported
454 );
455 assert_eq!(
456 DiagnosticCode::QuerySqlSurfaceMismatch.class(),
457 ErrorClass::Unsupported
458 );
459 assert_eq!(DiagnosticCode::QueryPlan.class(), ErrorClass::Query);
460 assert_eq!(
461 DiagnosticCode::StoreCorruption.class(),
462 ErrorClass::Corruption
463 );
464 }
465}