1use std::fmt;
13
14#[remain::sorted]
21#[derive(Clone, Copy, Eq, Hash, PartialEq)]
22pub enum DiagnosticCode {
23 QueryAccessRequirement,
24 QueryIntent,
25 QueryInvalidContinuationCursor,
26 QueryNotFound,
27 QueryNotUnique,
28 QueryNumericNotRepresentable,
29 QueryNumericOverflow,
30 QueryPlan,
31 QueryReadAdmission,
32 QueryResultShapeMismatch,
33 QuerySqlSurfaceMismatch,
34 QuerySqlWriteBoundary,
35 QueryUnknownAggregateTargetField,
36 QueryUnorderedPagination,
37 QueryUnsupportedProjection,
38 QueryUnsupportedSqlFeature,
39 QueryValidate,
40 RuntimeConflict,
41 RuntimeCorruption,
42 RuntimeIncompatiblePersistedFormat,
43 RuntimeInternal,
44 RuntimeInvariantViolation,
45 RuntimeNotFound,
46 RuntimeUnsupported,
47 SchemaDdlAdmission,
48 StoreCorruption,
49 StoreInvariantViolation,
50 StoreNotFound,
51}
52
53impl DiagnosticCode {
54 #[must_use]
56 pub const fn class(self) -> ErrorClass {
57 match self {
58 Self::StoreCorruption | Self::RuntimeCorruption => ErrorClass::Corruption,
59 Self::RuntimeIncompatiblePersistedFormat => ErrorClass::IncompatiblePersistedFormat,
60 Self::QueryNotFound | Self::StoreNotFound | Self::RuntimeNotFound => {
61 ErrorClass::NotFound
62 }
63 Self::RuntimeConflict => ErrorClass::Conflict,
64 Self::QueryUnsupportedSqlFeature
65 | Self::QueryUnknownAggregateTargetField
66 | Self::QueryUnsupportedProjection
67 | Self::QueryResultShapeMismatch
68 | Self::QuerySqlSurfaceMismatch
69 | Self::QuerySqlWriteBoundary
70 | Self::RuntimeUnsupported => ErrorClass::Unsupported,
71 Self::StoreInvariantViolation | Self::RuntimeInvariantViolation => {
72 ErrorClass::InvariantViolation
73 }
74 Self::RuntimeInternal => ErrorClass::Internal,
75 Self::QueryValidate
76 | Self::QueryIntent
77 | Self::QueryPlan
78 | Self::QueryReadAdmission
79 | Self::QueryAccessRequirement
80 | Self::QueryUnorderedPagination
81 | Self::QueryInvalidContinuationCursor
82 | Self::QueryNotUnique
83 | Self::QueryNumericOverflow
84 | Self::QueryNumericNotRepresentable
85 | Self::SchemaDdlAdmission => ErrorClass::Query,
86 }
87 }
88
89 #[must_use]
91 pub const fn origin(self) -> ErrorOrigin {
92 match self {
93 Self::StoreNotFound | Self::StoreCorruption | Self::StoreInvariantViolation => {
94 ErrorOrigin::Store
95 }
96 Self::RuntimeCorruption
97 | Self::RuntimeIncompatiblePersistedFormat
98 | Self::RuntimeInvariantViolation
99 | Self::RuntimeConflict
100 | Self::RuntimeNotFound
101 | Self::RuntimeUnsupported
102 | Self::RuntimeInternal => ErrorOrigin::Runtime,
103 Self::QueryValidate
104 | Self::QueryIntent
105 | Self::QueryPlan
106 | Self::QueryReadAdmission
107 | Self::QueryAccessRequirement
108 | Self::QueryUnorderedPagination
109 | Self::QueryInvalidContinuationCursor
110 | Self::QueryNotFound
111 | Self::QueryNotUnique
112 | Self::QueryNumericOverflow
113 | Self::QueryNumericNotRepresentable
114 | Self::QueryUnknownAggregateTargetField
115 | Self::QueryUnsupportedProjection
116 | Self::QueryResultShapeMismatch
117 | Self::QueryUnsupportedSqlFeature
118 | Self::QuerySqlSurfaceMismatch
119 | Self::QuerySqlWriteBoundary
120 | Self::SchemaDdlAdmission => ErrorOrigin::Query,
121 }
122 }
123
124 #[must_use]
126 pub const fn error_code(self) -> ErrorCode {
127 match self {
128 Self::QueryValidate => ErrorCode::QUERY_VALIDATE,
129 Self::QueryIntent => ErrorCode::QUERY_INTENT,
130 Self::QueryPlan => ErrorCode::QUERY_PLAN,
131 Self::QueryReadAdmission => ErrorCode::QUERY_READ_ADMISSION,
132 Self::QueryAccessRequirement => ErrorCode::QUERY_ACCESS_REQUIREMENT,
133 Self::QueryUnorderedPagination => ErrorCode::QUERY_UNORDERED_PAGINATION,
134 Self::QueryInvalidContinuationCursor => ErrorCode::QUERY_INVALID_CONTINUATION_CURSOR,
135 Self::QueryNotFound => ErrorCode::QUERY_NOT_FOUND,
136 Self::QueryNotUnique => ErrorCode::QUERY_NOT_UNIQUE,
137 Self::QueryNumericOverflow => ErrorCode::QUERY_NUMERIC_OVERFLOW,
138 Self::QueryNumericNotRepresentable => ErrorCode::QUERY_NUMERIC_NOT_REPRESENTABLE,
139 Self::QueryUnknownAggregateTargetField => {
140 ErrorCode::QUERY_UNKNOWN_AGGREGATE_TARGET_FIELD
141 }
142 Self::QueryUnsupportedProjection => ErrorCode::QUERY_UNSUPPORTED_PROJECTION,
143 Self::QueryResultShapeMismatch => ErrorCode::QUERY_RESULT_SHAPE_MISMATCH,
144 Self::QueryUnsupportedSqlFeature => ErrorCode::QUERY_UNSUPPORTED_SQL_FEATURE,
145 Self::QuerySqlSurfaceMismatch => ErrorCode::QUERY_SQL_SURFACE_MISMATCH,
146 Self::QuerySqlWriteBoundary => ErrorCode::QUERY_SQL_WRITE_BOUNDARY,
147 Self::SchemaDdlAdmission => ErrorCode::SCHEMA_DDL_ADMISSION,
148 Self::StoreNotFound => ErrorCode::STORE_NOT_FOUND,
149 Self::StoreCorruption => ErrorCode::STORE_CORRUPTION,
150 Self::StoreInvariantViolation => ErrorCode::STORE_INVARIANT_VIOLATION,
151 Self::RuntimeCorruption => ErrorCode::RUNTIME_CORRUPTION,
152 Self::RuntimeIncompatiblePersistedFormat => {
153 ErrorCode::RUNTIME_INCOMPATIBLE_PERSISTED_FORMAT
154 }
155 Self::RuntimeInvariantViolation => ErrorCode::RUNTIME_INVARIANT_VIOLATION,
156 Self::RuntimeConflict => ErrorCode::RUNTIME_CONFLICT,
157 Self::RuntimeNotFound => ErrorCode::RUNTIME_NOT_FOUND,
158 Self::RuntimeUnsupported => ErrorCode::RUNTIME_UNSUPPORTED,
159 Self::RuntimeInternal => ErrorCode::RUNTIME_INTERNAL,
160 }
161 }
162}
163
164impl fmt::Debug for DiagnosticCode {
165 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
166 fmt_compact_code(f, self.error_code().raw())
167 }
168}
169
170#[derive(Clone, Copy, Eq, Hash, PartialEq)]
182pub struct ErrorCode(u16);
183
184mod registry;
185
186impl ErrorCode {
187 #[must_use]
189 pub const fn from_raw(raw: u16) -> Self {
190 Self(raw)
191 }
192
193 #[must_use]
195 pub const fn raw(self) -> u16 {
196 self.0
197 }
198}
199
200impl fmt::Debug for ErrorCode {
201 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
202 fmt_compact_code(f, self.raw())
203 }
204}
205
206#[remain::sorted]
213#[derive(Clone, Copy, Eq, Hash, PartialEq)]
214pub enum ErrorClass {
215 Conflict,
216 Corruption,
217 IncompatiblePersistedFormat,
218 Internal,
219 InvariantViolation,
220 NotFound,
221 Query,
222 Unsupported,
223}
224
225impl ErrorClass {
226 #[must_use]
228 pub const fn wire_code(self) -> u8 {
229 match self {
230 Self::Query => 1,
231 Self::Corruption => 2,
232 Self::IncompatiblePersistedFormat => 3,
233 Self::NotFound => 4,
234 Self::Internal => 5,
235 Self::Conflict => 6,
236 Self::Unsupported => 7,
237 Self::InvariantViolation => 8,
238 }
239 }
240
241 #[must_use]
243 pub const fn from_wire_code(code: u8) -> Option<Self> {
244 match code {
245 1 => Some(Self::Query),
246 2 => Some(Self::Corruption),
247 3 => Some(Self::IncompatiblePersistedFormat),
248 4 => Some(Self::NotFound),
249 5 => Some(Self::Internal),
250 6 => Some(Self::Conflict),
251 7 => Some(Self::Unsupported),
252 8 => Some(Self::InvariantViolation),
253 _ => None,
254 }
255 }
256}
257
258impl fmt::Debug for ErrorClass {
259 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
260 fmt_compact_code(f, u16::from(self.wire_code()))
261 }
262}
263
264#[remain::sorted]
271#[derive(Clone, Copy, Eq, Hash, PartialEq)]
272pub enum ErrorOrigin {
273 Cursor,
274 Executor,
275 Identity,
276 Index,
277 Interface,
278 Planner,
279 Query,
280 Recovery,
281 Response,
282 Runtime,
283 Serialize,
284 Store,
285}
286
287impl ErrorOrigin {
288 #[must_use]
290 pub const fn wire_code(self) -> u8 {
291 match self {
292 Self::Cursor => 1,
293 Self::Executor => 2,
294 Self::Identity => 3,
295 Self::Index => 4,
296 Self::Interface => 5,
297 Self::Planner => 6,
298 Self::Query => 7,
299 Self::Recovery => 8,
300 Self::Response => 9,
301 Self::Runtime => 10,
302 Self::Serialize => 11,
303 Self::Store => 12,
304 }
305 }
306
307 #[must_use]
309 pub const fn from_known_wire_code(code: u8) -> Option<Self> {
310 match code {
311 1 => Some(Self::Cursor),
312 2 => Some(Self::Executor),
313 3 => Some(Self::Identity),
314 4 => Some(Self::Index),
315 5 => Some(Self::Interface),
316 6 => Some(Self::Planner),
317 7 => Some(Self::Query),
318 8 => Some(Self::Recovery),
319 9 => Some(Self::Response),
320 10 => Some(Self::Runtime),
321 11 => Some(Self::Serialize),
322 12 => Some(Self::Store),
323 _ => None,
324 }
325 }
326
327 #[must_use]
332 pub const fn from_wire_code(code: u8) -> Self {
333 match Self::from_known_wire_code(code) {
334 Some(origin) => origin,
335 None => Self::Runtime,
336 }
337 }
338}
339
340impl fmt::Debug for ErrorOrigin {
341 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
342 fmt_compact_code(f, u16::from(self.wire_code()))
343 }
344}
345
346#[repr(u16)]
353#[derive(Clone, Copy, Eq, Hash, PartialEq)]
354pub enum QueryErrorKind {
355 Validate,
356 Intent,
357 Plan,
358 AccessRequirement,
359 UnorderedPagination,
360 InvalidContinuationCursor,
361 NotFound,
362 NotUnique,
363}
364
365impl fmt::Debug for QueryErrorKind {
366 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
367 fmt_compact_code(f, *self as u16)
368 }
369}
370
371#[repr(u16)]
379#[derive(Clone, Copy, Eq, Hash, PartialEq)]
380pub enum QueryProjectionCode {
381 NumericLiteralRequired,
382 NumericScaleArguments,
383 NestedFieldPathPreview,
384 CaseConditionBooleanRequired,
385 NumericInputRequired,
386 TextOrBlobInputRequired,
387 TextInputRequired,
388 TextOrNullArgumentRequired,
389 IntegerOrNullArgumentRequired,
390 UnaryOperandIncompatible,
391 BinaryOperandsIncompatible,
392}
393
394impl fmt::Debug for QueryProjectionCode {
395 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
396 fmt_compact_code(f, *self as u16)
397 }
398}
399
400#[repr(u16)]
408#[derive(Clone, Copy, Eq, Hash, PartialEq)]
409pub enum QueryReadAdmissionCode {
410 PublicQueryRequiresLimit,
411 PublicQueryRequiresIndex,
412 UnboundedFullScanRejected,
413 SortRequiresMaterialization,
414 GroupedQueryRequiresLimits,
415 GroupedQueryExceedsBudget,
416 DiagnosticLaneDoesNotExecute,
417 ReturnedRowBoundExceedsPolicy,
418 PrimaryKeyInputExceedsPolicy,
419}
420
421impl fmt::Debug for QueryReadAdmissionCode {
422 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
423 fmt_compact_code(f, *self as u16)
424 }
425}
426
427#[repr(u16)]
435#[derive(Clone, Copy, Eq, Hash, PartialEq)]
436pub enum QueryResultShapeCode {
437 ExpectedRows,
438 ExpectedGroupedRows,
439}
440
441impl fmt::Debug for QueryResultShapeCode {
442 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
443 fmt_compact_code(f, *self as u16)
444 }
445}
446
447#[repr(u16)]
454#[derive(Clone, Copy, Eq, Hash, PartialEq)]
455pub enum RuntimeErrorKind {
456 Corruption,
457 IncompatiblePersistedFormat,
458 InvariantViolation,
459 Conflict,
460 NotFound,
461 Unsupported,
462 Internal,
463}
464
465impl fmt::Debug for RuntimeErrorKind {
466 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
467 fmt_compact_code(f, *self as u16)
468 }
469}
470
471#[repr(u16)]
479#[derive(Clone, Copy, Eq, Hash, PartialEq)]
480pub enum RuntimeBoundaryCode {
481 SqlSurfaceControllerRequired,
482 SchemaSurfaceControllerRequired,
483 SqlQueryNoConfiguredEntities,
484 SqlQueryEntityNotConfigured,
485 SqlDdlTargetRequired,
486 SqlDdlEntityNotConfigured,
487 QueryResponseRowsRequired,
488 QueryResponseGroupedRowsRequired,
489 RowProjectionFieldNotConfigured,
490 SqlIntrospectionDisabled,
491 MutationRequiredFieldMissing,
493 MutationManagedTimestampRegression,
495 PersistedRowLayoutOutsideAcceptedWindow,
497 PersistedRowSlotCountMismatch,
499 GeneratedFieldAfterDdlField,
501 JournalMutationRevisionExhausted,
503 ConstraintViolation,
505 AcceptedRowConstraintProgramCorrupt,
507 ConstraintActivationWriteBlocked,
509 GeneratedConstraintActivationStale,
511 MutationDatabaseOwnedFieldExplicit,
513}
514
515impl fmt::Debug for RuntimeBoundaryCode {
516 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
517 fmt_compact_code(f, *self as u16)
518 }
519}
520
521#[repr(u16)]
529#[derive(Clone, Copy, Eq, Hash, PartialEq)]
530pub enum SqlFeatureCode {
531 AggregateFilterClause,
532 AlterStatementBeyondAlterTable,
533 AlterTableAddColumnDuplicateDefault,
534 AlterTableAddColumnModifiers,
535 AlterTableAddStatementBeyondAddColumn,
536 AlterTableAlterColumnDropUnsupportedAction,
537 AlterTableAlterColumnModifiers,
538 AlterTableAlterColumnSetUnsupportedAction,
539 AlterTableAlterColumnUnsupportedAction,
540 AlterTableAlterStatementBeyondAlterColumn,
541 AlterTableDropColumnIfExistsSyntax,
542 AlterTableDropColumnModifiers,
543 AlterTableDropStatementBeyondDropColumn,
544 AlterTableRenameColumnMissingTo,
545 AlterTableRenameColumnModifiers,
546 AlterTableRenameStatementBeyondRenameColumn,
547 AlterTableUnsupportedOperation,
548 ColumnAlias,
549 CreateIndexIfNotExistsSyntax,
550 CreateIndexKeyOrderingModifiers,
551 CreateIndexModifiers,
552 CreateStatementBeyondCreateIndex,
553 DescribeModifier,
554 DdlSchemaVersionDuplicateExpectedClause,
555 DdlSchemaVersionDuplicateSetClause,
556 DropIndexModifiers,
557 DropIndexIfExistsSyntax,
558 DropStatementBeyondDropIndex,
559 ExpressionIndexUnsupportedFunction,
560 Having,
561 Insert,
562 Join,
563 LikePatternBeyondTrailingPrefix,
564 LowerFieldPredicateUnsupported,
565 MultiStatementSql,
566 NestedAggregateInput,
567 NestedProjectionFunctionInArithmetic,
568 OrderByUnsupportedForm,
569 Other,
570 PredicateStartsWithFirstArgument,
571 QuotedIdentifiers,
572 ReturningUnsupportedShape,
573 ScalarFunctionExpressionPosition,
574 ScaleTakingNumericFunctionExpressionPosition,
575 SearchedCaseGroupedOrderBy,
576 ShowColumnsModifiers,
577 ShowEntitiesModifiers,
578 ShowIndexesModifiers,
579 ShowMemoryModifiers,
580 ShowStoresModifiers,
581 ShowUnsupportedCommand,
582 SimpleCaseExpression,
583 StandaloneLiteralProjectionItem,
584 SupportedGroupedOrderByExpressionFamily,
585 SupportedOrderByExpressionFamily,
586 UnionIntersectExcept,
587 UnsupportedFunctionNamespace,
588 Update,
589 UpperFieldPredicateUnsupported,
590 WindowFunction,
591 With,
592 NumericScaleFunctionArguments,
593 OrderByFieldNotOrderable,
594 ShowConstraintsModifiers,
595 AlterTableAddConstraintBeyondCheck,
596 AlterTableAddConstraintModifiers,
597 AlterTableDropConstraintIfExistsSyntax,
598 AlterTableDropConstraintModifiers,
599 AlterTableValidateBeyondConstraint,
600 AlterTableValidateConstraintModifiers,
601}
602
603impl fmt::Debug for SqlFeatureCode {
604 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
605 fmt_compact_code(f, *self as u16)
606 }
607}
608
609#[repr(u16)]
618#[derive(Clone, Copy, Eq, Hash, PartialEq)]
619pub enum SqlLoweringCode {
620 EntityMismatch,
621 SelectProjectionShape,
622 SelectDistinct,
623 DistinctOrderByProjection,
624 GlobalAggregateProjection,
625 GlobalAggregateGroupBy,
626 SelectGroupByShape,
627 GroupedProjectionExplicitListRequired,
628 GroupedProjectionAggregateRequired,
629 GroupedProjectionNonGroupField,
630 GroupedProjectionScalarAfterAggregate,
631 HavingRequiresGroupBy,
632 SelectHavingShape,
633 AggregateInputExpressions,
634 WhereExpressionShape,
635 ParameterPlacement,
636 SqlDdlExecutionUnsupported,
637}
638
639impl fmt::Debug for SqlLoweringCode {
640 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
641 fmt_compact_code(f, *self as u16)
642 }
643}
644
645#[repr(u16)]
653#[derive(Clone, Copy, Eq, Hash, PartialEq)]
654pub enum SqlSurfaceMismatchCode {
655 QueryRejectsInsert,
656 QueryRejectsUpdate,
657 QueryRejectsDelete,
658 MutationRejectsSelect,
659 MutationRejectsExplain,
660 MutationRejectsDescribe,
661 MutationRejectsShowIndexes,
662 MutationRejectsShowColumns,
663 MutationRejectsShowEntities,
664 MutationRejectsShowStores,
665 MutationRejectsShowMemory,
666 MutationRequiresExplicitUpdateIntent,
667 MutationRejectsShowConstraints,
668}
669
670impl fmt::Debug for SqlSurfaceMismatchCode {
671 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
672 fmt_compact_code(f, *self as u16)
673 }
674}
675
676#[repr(u16)]
684#[derive(Clone, Copy, Eq, Hash, PartialEq)]
685pub enum SqlWriteBoundaryCode {
686 PrimaryKeyLiteralShape,
687 PrimaryKeyLiteralIncompatible,
688 MissingPrimaryKey,
689 MissingRequiredFields,
690 ExplicitManagedField,
691 ExplicitGeneratedField,
692 InsertSelectRequiresScalar,
693 InsertSelectAggregateProjection,
694 InsertSelectWidthMismatch,
695 UpdatePrimaryKeyMutation,
696 InvalidFieldLiteral,
697 UnknownReturningField,
698 DuplicateReturningField,
699 UpdateMissingWherePredicate,
700 WriteOrderByUnsupportedShape,
701 ReturningResponseTooLarge,
702 ReturningRowsTooMany,
703 StagedRowsTooMany,
704 InsertDefaultRequiredField,
705 UpdateDefaultRequiredField,
706 UpdateDefaultDatabaseOwnedField,
707 ExactUpdateAssertionRequired,
708 ExactUpdateAssertionTooHigh,
709 ExactUpdateAffectedRowsExceeded,
710 ExactUpdateWindowUnsupported,
711 ExactUpdateScanBudgetExceeded,
712 ResumableUpdateWindowUnsupported,
713 ResumableUpdateReturningUnsupported,
714 ResumableUpdateRequiresJournaledStore,
715 ResumableUpdateAssignedFieldHasGlobalConstraint,
716 ResumableUpdateScopeDependsOnAssignedField,
717 ResumableUpdateScopeDependencyUnknown,
718 ResumableUpdateContinuationMalformed,
719 ResumableUpdateContinuationTargetMismatch,
720 ResumableUpdateContinuationSchemaMismatch,
721 ResumableUpdateContinuationScopeMismatch,
722 ResumableUpdateContinuationPatchMismatch,
723 ResumableUpdateContinuationBatchPolicyMismatch,
724 ResumableUpdateSingleRowResourceExceeded,
725 ResumableUpdateManagedFieldHasGlobalConstraint,
726 ResumableUpdateContinuationOperationMismatch,
727}
728
729impl fmt::Debug for SqlWriteBoundaryCode {
730 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
731 fmt_compact_code(f, *self as u16)
732 }
733}
734
735#[repr(u16)]
743#[derive(Clone, Copy, Eq, Hash, PartialEq)]
744pub enum SchemaDdlAdmissionCode {
745 MissingExpectedSchemaVersion,
746 MissingNextSchemaVersion,
747 StaleExpectedSchemaVersion,
748 InvalidExpectedSchemaVersion,
749 InvalidNextSchemaVersion,
750 AcceptedSchemaChangeWithoutVersionBump,
751 EmptyVersionBump,
752 VersionGap,
753 VersionRollback,
754 FingerprintMethodMismatch,
755 UnsupportedTransitionClass,
756 PhysicalRunnerMissing,
757 ValidationFailed,
758 PublicationRaceLost,
759 InvalidAddColumnDefault,
760 InvalidAlterColumnDefault,
761 GeneratedIndexDropRejected,
762 SchemaRewriteRequiresMigration,
763 SchemaTransitionBudgetExceeded,
764 GeneratedFieldDefaultChangeRejected,
765 GeneratedFieldNullabilityChangeRejected,
766 RowLayoutVersionExhausted,
767}
768
769impl fmt::Debug for SchemaDdlAdmissionCode {
770 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
771 fmt_compact_code(f, *self as u16)
772 }
773}
774
775#[remain::sorted]
782#[derive(Clone, Copy, Eq, PartialEq)]
783pub enum DiagnosticDetail {
784 QueryKind { kind: QueryErrorKind },
785 QueryProjection { reason: QueryProjectionCode },
786 QueryReadAdmission { reason: QueryReadAdmissionCode },
787 QueryResultShape { reason: QueryResultShapeCode },
788 RuntimeBoundary { boundary: RuntimeBoundaryCode },
789 RuntimeKind { kind: RuntimeErrorKind },
790 SchemaDdlAdmission { reason: SchemaDdlAdmissionCode },
791 SqlLowering { reason: SqlLoweringCode },
792 SqlSurfaceMismatch { mismatch: SqlSurfaceMismatchCode },
793 SqlWriteBoundary { boundary: SqlWriteBoundaryCode },
794 UnsupportedSqlFeature { feature: SqlFeatureCode },
795}
796
797impl fmt::Debug for DiagnosticDetail {
798 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
799 fmt_compact_code(
800 f,
801 ErrorCode::from_parts(self.diagnostic_code(), Some(*self)).raw(),
802 )
803 }
804}
805
806#[derive(Clone, Eq, PartialEq)]
813pub struct Diagnostic {
814 code: DiagnosticCode,
815 origin: ErrorOrigin,
816 detail: Option<DiagnosticDetail>,
817}
818
819impl Diagnostic {
820 #[must_use]
822 pub const fn new(
823 code: DiagnosticCode,
824 origin: ErrorOrigin,
825 detail: Option<DiagnosticDetail>,
826 ) -> Self {
827 Self {
828 code,
829 origin,
830 detail,
831 }
832 }
833
834 #[must_use]
836 pub const fn from_code(code: DiagnosticCode) -> Self {
837 Self::new(code, code.origin(), None)
838 }
839
840 #[must_use]
842 pub const fn code(&self) -> DiagnosticCode {
843 self.code
844 }
845
846 #[must_use]
848 pub const fn class(&self) -> ErrorClass {
849 self.code.class()
850 }
851
852 #[must_use]
854 pub const fn origin(&self) -> ErrorOrigin {
855 self.origin
856 }
857
858 #[must_use]
860 pub const fn detail(&self) -> Option<&DiagnosticDetail> {
861 self.detail.as_ref()
862 }
863
864 #[must_use]
866 pub const fn error_code(&self) -> ErrorCode {
867 ErrorCode::from_parts(self.code, self.detail)
868 }
869}
870
871impl fmt::Debug for Diagnostic {
872 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
873 write!(f, "{}@{}", self.error_code().raw(), self.origin.wire_code())
874 }
875}
876
877fn fmt_compact_code(f: &mut fmt::Formatter<'_>, raw: u16) -> fmt::Result {
878 write!(f, "{raw}")
879}
880
881#[cfg(test)]
882mod tests {
883 use super::{
884 Diagnostic, DiagnosticCode, DiagnosticDetail, ErrorClass, ErrorCode, ErrorOrigin,
885 QueryProjectionCode, QueryReadAdmissionCode, SqlFeatureCode, SqlLoweringCode,
886 SqlWriteBoundaryCode,
887 registry::{DETAIL_ERROR_CODES, ORDERED_ERROR_CODES},
888 };
889
890 #[test]
891 fn diagnostic_from_code_uses_default_origin() {
892 let diagnostic = Diagnostic::from_code(DiagnosticCode::QueryPlan);
893
894 assert_eq!(diagnostic.code(), DiagnosticCode::QueryPlan);
895 assert_eq!(diagnostic.origin(), ErrorOrigin::Query);
896 }
897
898 #[test]
899 fn diagnostic_code_reports_broad_class() {
900 assert_eq!(
901 DiagnosticCode::QueryUnsupportedSqlFeature.class(),
902 ErrorClass::Unsupported
903 );
904 assert_eq!(
905 DiagnosticCode::QuerySqlSurfaceMismatch.class(),
906 ErrorClass::Unsupported
907 );
908 assert_eq!(DiagnosticCode::QueryPlan.class(), ErrorClass::Query);
909 assert_eq!(
910 DiagnosticCode::StoreCorruption.class(),
911 ErrorClass::Corruption
912 );
913 }
914
915 #[test]
916 fn class_and_origin_wire_codes_round_trip() {
917 for (class, raw) in [
918 (ErrorClass::Query, 1),
919 (ErrorClass::Corruption, 2),
920 (ErrorClass::IncompatiblePersistedFormat, 3),
921 (ErrorClass::NotFound, 4),
922 (ErrorClass::Internal, 5),
923 (ErrorClass::Conflict, 6),
924 (ErrorClass::Unsupported, 7),
925 (ErrorClass::InvariantViolation, 8),
926 ] {
927 assert_eq!(class.wire_code(), raw);
928 assert_eq!(ErrorClass::from_wire_code(raw), Some(class));
929 assert_eq!(format!("{class:?}"), raw.to_string());
930 }
931
932 for (origin, raw) in [
933 (ErrorOrigin::Cursor, 1),
934 (ErrorOrigin::Executor, 2),
935 (ErrorOrigin::Identity, 3),
936 (ErrorOrigin::Index, 4),
937 (ErrorOrigin::Interface, 5),
938 (ErrorOrigin::Planner, 6),
939 (ErrorOrigin::Query, 7),
940 (ErrorOrigin::Recovery, 8),
941 (ErrorOrigin::Response, 9),
942 (ErrorOrigin::Runtime, 10),
943 (ErrorOrigin::Serialize, 11),
944 (ErrorOrigin::Store, 12),
945 ] {
946 assert_eq!(origin.wire_code(), raw);
947 assert_eq!(ErrorOrigin::from_known_wire_code(raw), Some(origin));
948 assert_eq!(ErrorOrigin::from_wire_code(raw), origin);
949 assert_eq!(format!("{origin:?}"), raw.to_string());
950 }
951
952 assert_eq!(ErrorClass::from_wire_code(0), None);
953 assert_eq!(ErrorOrigin::from_known_wire_code(0), None);
954 assert_eq!(ErrorOrigin::from_wire_code(0), ErrorOrigin::Runtime);
955 }
956
957 #[test]
958 fn public_error_codes_are_sequential() {
959 let first = ORDERED_ERROR_CODES
960 .first()
961 .expect("public error-code registry is non-empty")
962 .raw();
963
964 assert_eq!(first, 1);
965
966 for (index, code) in ORDERED_ERROR_CODES.iter().enumerate() {
967 let expected = first + u16::try_from(index).expect("test error-code index fits u16");
968 assert_eq!(code.raw(), expected);
969 assert_eq!(ErrorCode::known(code.raw()), Some(*code));
970 assert!(code.is_known());
971 }
972
973 let last = ORDERED_ERROR_CODES
974 .last()
975 .expect("public error-code registry is non-empty")
976 .raw();
977
978 assert_eq!(last, 234);
979 }
980
981 #[test]
982 fn all_public_error_codes_round_trip_through_diagnostic_parts() {
983 let first = ORDERED_ERROR_CODES
984 .first()
985 .expect("public error-code registry is non-empty")
986 .raw();
987 let last = ORDERED_ERROR_CODES
988 .last()
989 .expect("public error-code registry is non-empty")
990 .raw();
991
992 for raw in first..=last {
993 let code = ErrorCode::from_raw(raw);
994 let diagnostic_code = code.diagnostic_code();
995 let diagnostic_detail = code.diagnostic_detail();
996 let rebuilt = ErrorCode::from_parts(diagnostic_code, diagnostic_detail);
997
998 assert_eq!(rebuilt.raw(), raw);
999
1000 let diagnostic = code.diagnostic(ErrorOrigin::Runtime);
1001
1002 assert_eq!(diagnostic.code(), diagnostic_code);
1003 assert_eq!(diagnostic.detail(), diagnostic_detail.as_ref());
1004 assert_eq!(diagnostic.error_code().raw(), raw);
1005 }
1006 }
1007
1008 #[test]
1009 fn invalid_raw_error_codes_fail_closed_to_runtime_internal() {
1010 for raw in [0, 235, u16::MAX] {
1011 let code = ErrorCode::from_raw(raw);
1012
1013 assert_eq!(ErrorCode::known(raw), None);
1014 assert!(!code.is_known());
1015 assert_eq!(code.diagnostic_code(), DiagnosticCode::RuntimeInternal);
1016 assert_eq!(code.diagnostic_detail(), None);
1017 assert_eq!(code.class(), ErrorClass::Internal);
1018
1019 let diagnostic = code.diagnostic(ErrorOrigin::Query);
1020
1021 assert_eq!(diagnostic.code(), DiagnosticCode::RuntimeInternal);
1022 assert_eq!(diagnostic.origin(), ErrorOrigin::Query);
1023 assert_eq!(diagnostic.detail(), None);
1024 assert_eq!(diagnostic.error_code(), ErrorCode::RUNTIME_INTERNAL);
1025 }
1026 }
1027
1028 #[test]
1029 fn from_parts_requires_detail_to_match_broad_code() {
1030 let detail = Some(DiagnosticDetail::UnsupportedSqlFeature {
1031 feature: SqlFeatureCode::Join,
1032 });
1033
1034 assert_eq!(
1035 ErrorCode::from_parts(DiagnosticCode::QueryUnsupportedSqlFeature, detail),
1036 ErrorCode::SQL_FEATURE_JOIN
1037 );
1038 assert_eq!(
1039 ErrorCode::from_parts(DiagnosticCode::QueryPlan, detail),
1040 ErrorCode::QUERY_PLAN
1041 );
1042 }
1043
1044 #[test]
1045 fn detail_bearing_registry_entries_round_trip_directly() {
1046 assert!(!DETAIL_ERROR_CODES.is_empty());
1047
1048 for &(code, diagnostic_code, detail) in DETAIL_ERROR_CODES {
1049 assert_eq!(ErrorCode::from_parts(diagnostic_code, Some(detail)), code);
1050 assert_eq!(code.diagnostic_code(), diagnostic_code);
1051 assert_eq!(code.diagnostic_detail(), Some(detail));
1052 assert_eq!(detail.diagnostic_code(), diagnostic_code);
1053 }
1054 }
1055
1056 #[test]
1057 fn diagnostic_detail_reports_generated_broad_code() {
1058 let detail = DiagnosticDetail::UnsupportedSqlFeature {
1059 feature: SqlFeatureCode::Join,
1060 };
1061
1062 assert_eq!(
1063 detail.diagnostic_code(),
1064 DiagnosticCode::QueryUnsupportedSqlFeature
1065 );
1066 assert_eq!(format!("{detail:?}"), "65");
1067 }
1068
1069 #[test]
1070 fn public_error_codes_reconstruct_shifted_details() {
1071 assert_eq!(
1072 ErrorCode::QUERY_UNKNOWN_AGGREGATE_TARGET_FIELD.diagnostic_code(),
1073 DiagnosticCode::QueryUnknownAggregateTargetField
1074 );
1075 assert_eq!(
1076 ErrorCode::SQL_FEATURE_JOIN.diagnostic_detail(),
1077 Some(DiagnosticDetail::UnsupportedSqlFeature {
1078 feature: SqlFeatureCode::Join,
1079 })
1080 );
1081 assert_eq!(
1082 ErrorCode::QUERY_PROJECTION_NUMERIC_LITERAL_REQUIRED.diagnostic_detail(),
1083 Some(DiagnosticDetail::QueryProjection {
1084 reason: QueryProjectionCode::NumericLiteralRequired,
1085 })
1086 );
1087 assert_eq!(
1088 ErrorCode::QUERY_READ_PUBLIC_REQUIRES_LIMIT.diagnostic_detail(),
1089 Some(DiagnosticDetail::QueryReadAdmission {
1090 reason: QueryReadAdmissionCode::PublicQueryRequiresLimit,
1091 })
1092 );
1093 assert_eq!(
1094 ErrorCode::SQL_LOWERING_DISTINCT_ORDER_BY_PROJECTION.diagnostic_detail(),
1095 Some(DiagnosticDetail::SqlLowering {
1096 reason: SqlLoweringCode::DistinctOrderByProjection,
1097 })
1098 );
1099 assert_eq!(
1100 ErrorCode::SQL_WRITE_RETURNING_RESPONSE_TOO_LARGE.diagnostic_detail(),
1101 Some(DiagnosticDetail::SqlWriteBoundary {
1102 boundary: SqlWriteBoundaryCode::ReturningResponseTooLarge,
1103 })
1104 );
1105 assert_eq!(
1106 ErrorCode::SQL_WRITE_RETURNING_ROWS_TOO_MANY.diagnostic_detail(),
1107 Some(DiagnosticDetail::SqlWriteBoundary {
1108 boundary: SqlWriteBoundaryCode::ReturningRowsTooMany,
1109 })
1110 );
1111 }
1112}