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 PersistedRowLayoutOutsideAcceptedWindow,
495 PersistedRowSlotCountMismatch,
497 GeneratedFieldAfterDdlField,
499}
500
501impl fmt::Debug for RuntimeBoundaryCode {
502 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
503 fmt_compact_code(f, *self as u16)
504 }
505}
506
507#[repr(u16)]
515#[derive(Clone, Copy, Eq, Hash, PartialEq)]
516pub enum SqlFeatureCode {
517 AggregateFilterClause,
518 AlterStatementBeyondAlterTable,
519 AlterTableAddColumnDuplicateDefault,
520 AlterTableAddColumnModifiers,
521 AlterTableAddStatementBeyondAddColumn,
522 AlterTableAlterColumnDropUnsupportedAction,
523 AlterTableAlterColumnModifiers,
524 AlterTableAlterColumnSetUnsupportedAction,
525 AlterTableAlterColumnUnsupportedAction,
526 AlterTableAlterStatementBeyondAlterColumn,
527 AlterTableDropColumnIfExistsSyntax,
528 AlterTableDropColumnModifiers,
529 AlterTableDropStatementBeyondDropColumn,
530 AlterTableRenameColumnMissingTo,
531 AlterTableRenameColumnModifiers,
532 AlterTableRenameStatementBeyondRenameColumn,
533 AlterTableUnsupportedOperation,
534 ColumnAlias,
535 CreateIndexIfNotExistsSyntax,
536 CreateIndexKeyOrderingModifiers,
537 CreateIndexModifiers,
538 CreateStatementBeyondCreateIndex,
539 DescribeModifier,
540 DdlSchemaVersionDuplicateExpectedClause,
541 DdlSchemaVersionDuplicateSetClause,
542 DropIndexModifiers,
543 DropIndexIfExistsSyntax,
544 DropStatementBeyondDropIndex,
545 ExpressionIndexUnsupportedFunction,
546 Having,
547 Insert,
548 Join,
549 LikePatternBeyondTrailingPrefix,
550 LowerFieldPredicateUnsupported,
551 MultiStatementSql,
552 NestedAggregateInput,
553 NestedProjectionFunctionInArithmetic,
554 OrderByUnsupportedForm,
555 Other,
556 PredicateStartsWithFirstArgument,
557 QuotedIdentifiers,
558 ReturningUnsupportedShape,
559 ScalarFunctionExpressionPosition,
560 ScaleTakingNumericFunctionExpressionPosition,
561 SearchedCaseGroupedOrderBy,
562 ShowColumnsModifiers,
563 ShowEntitiesModifiers,
564 ShowIndexesModifiers,
565 ShowMemoryModifiers,
566 ShowStoresModifiers,
567 ShowUnsupportedCommand,
568 SimpleCaseExpression,
569 StandaloneLiteralProjectionItem,
570 SupportedGroupedOrderByExpressionFamily,
571 SupportedOrderByExpressionFamily,
572 UnionIntersectExcept,
573 UnsupportedFunctionNamespace,
574 Update,
575 UpperFieldPredicateUnsupported,
576 WindowFunction,
577 With,
578 NumericScaleFunctionArguments,
579 OrderByFieldNotOrderable,
580}
581
582impl fmt::Debug for SqlFeatureCode {
583 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
584 fmt_compact_code(f, *self as u16)
585 }
586}
587
588#[repr(u16)]
597#[derive(Clone, Copy, Eq, Hash, PartialEq)]
598pub enum SqlLoweringCode {
599 EntityMismatch,
600 SelectProjectionShape,
601 SelectDistinct,
602 DistinctOrderByProjection,
603 GlobalAggregateProjection,
604 GlobalAggregateGroupBy,
605 SelectGroupByShape,
606 GroupedProjectionExplicitListRequired,
607 GroupedProjectionAggregateRequired,
608 GroupedProjectionNonGroupField,
609 GroupedProjectionScalarAfterAggregate,
610 HavingRequiresGroupBy,
611 SelectHavingShape,
612 AggregateInputExpressions,
613 WhereExpressionShape,
614 ParameterPlacement,
615 SqlDdlExecutionUnsupported,
616}
617
618impl fmt::Debug for SqlLoweringCode {
619 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
620 fmt_compact_code(f, *self as u16)
621 }
622}
623
624#[repr(u16)]
632#[derive(Clone, Copy, Eq, Hash, PartialEq)]
633pub enum SqlSurfaceMismatchCode {
634 QueryRejectsInsert,
635 QueryRejectsUpdate,
636 QueryRejectsDelete,
637 UpdateRejectsSelect,
638 UpdateRejectsExplain,
639 UpdateRejectsDescribe,
640 UpdateRejectsShowIndexes,
641 UpdateRejectsShowColumns,
642 UpdateRejectsShowEntities,
643 UpdateRejectsShowStores,
644 UpdateRejectsShowMemory,
645}
646
647impl fmt::Debug for SqlSurfaceMismatchCode {
648 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
649 fmt_compact_code(f, *self as u16)
650 }
651}
652
653#[repr(u16)]
661#[derive(Clone, Copy, Eq, Hash, PartialEq)]
662pub enum SqlWriteBoundaryCode {
663 PrimaryKeyLiteralShape,
664 PrimaryKeyLiteralIncompatible,
665 MissingPrimaryKey,
666 MissingRequiredFields,
667 ExplicitManagedField,
668 ExplicitGeneratedField,
669 InsertSelectRequiresScalar,
670 InsertSelectAggregateProjection,
671 InsertSelectWidthMismatch,
672 UpdatePrimaryKeyMutation,
673 InvalidFieldLiteral,
674 UnknownReturningField,
675 DuplicateReturningField,
676 UpdateMissingWherePredicate,
677 WriteOrderByUnsupportedShape,
678 ReturningResponseTooLarge,
679 ReturningRowsTooMany,
680 StagedRowsTooMany,
681 InsertDefaultRequiredField,
682 UpdateDefaultRequiredField,
683 UpdateDefaultDatabaseOwnedField,
684}
685
686impl fmt::Debug for SqlWriteBoundaryCode {
687 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
688 fmt_compact_code(f, *self as u16)
689 }
690}
691
692#[repr(u16)]
700#[derive(Clone, Copy, Eq, Hash, PartialEq)]
701pub enum SchemaDdlAdmissionCode {
702 MissingExpectedSchemaVersion,
703 MissingNextSchemaVersion,
704 StaleExpectedSchemaVersion,
705 InvalidExpectedSchemaVersion,
706 InvalidNextSchemaVersion,
707 AcceptedSchemaChangeWithoutVersionBump,
708 EmptyVersionBump,
709 VersionGap,
710 VersionRollback,
711 FingerprintMethodMismatch,
712 UnsupportedTransitionClass,
713 PhysicalRunnerMissing,
714 ValidationFailed,
715 PublicationRaceLost,
716 InvalidAddColumnDefault,
717 InvalidAlterColumnDefault,
718 GeneratedIndexDropRejected,
719 SchemaRewriteRequiresMigration,
720 SchemaTransitionBudgetExceeded,
721 GeneratedFieldDefaultChangeRejected,
722 GeneratedFieldNullabilityChangeRejected,
723 SetNotNullValidationFailed,
724 RowLayoutVersionExhausted,
725}
726
727impl fmt::Debug for SchemaDdlAdmissionCode {
728 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
729 fmt_compact_code(f, *self as u16)
730 }
731}
732
733#[remain::sorted]
740#[derive(Clone, Copy, Eq, PartialEq)]
741pub enum DiagnosticDetail {
742 QueryKind { kind: QueryErrorKind },
743 QueryProjection { reason: QueryProjectionCode },
744 QueryReadAdmission { reason: QueryReadAdmissionCode },
745 QueryResultShape { reason: QueryResultShapeCode },
746 RuntimeBoundary { boundary: RuntimeBoundaryCode },
747 RuntimeKind { kind: RuntimeErrorKind },
748 SchemaDdlAdmission { reason: SchemaDdlAdmissionCode },
749 SqlLowering { reason: SqlLoweringCode },
750 SqlSurfaceMismatch { mismatch: SqlSurfaceMismatchCode },
751 SqlWriteBoundary { boundary: SqlWriteBoundaryCode },
752 UnsupportedSqlFeature { feature: SqlFeatureCode },
753}
754
755impl fmt::Debug for DiagnosticDetail {
756 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
757 fmt_compact_code(
758 f,
759 ErrorCode::from_parts(self.diagnostic_code(), Some(*self)).raw(),
760 )
761 }
762}
763
764#[derive(Clone, Eq, PartialEq)]
771pub struct Diagnostic {
772 code: DiagnosticCode,
773 origin: ErrorOrigin,
774 detail: Option<DiagnosticDetail>,
775}
776
777impl Diagnostic {
778 #[must_use]
780 pub const fn new(
781 code: DiagnosticCode,
782 origin: ErrorOrigin,
783 detail: Option<DiagnosticDetail>,
784 ) -> Self {
785 Self {
786 code,
787 origin,
788 detail,
789 }
790 }
791
792 #[must_use]
794 pub const fn from_code(code: DiagnosticCode) -> Self {
795 Self::new(code, code.origin(), None)
796 }
797
798 #[must_use]
800 pub const fn code(&self) -> DiagnosticCode {
801 self.code
802 }
803
804 #[must_use]
806 pub const fn class(&self) -> ErrorClass {
807 self.code.class()
808 }
809
810 #[must_use]
812 pub const fn origin(&self) -> ErrorOrigin {
813 self.origin
814 }
815
816 #[must_use]
818 pub const fn detail(&self) -> Option<&DiagnosticDetail> {
819 self.detail.as_ref()
820 }
821
822 #[must_use]
824 pub const fn error_code(&self) -> ErrorCode {
825 ErrorCode::from_parts(self.code, self.detail)
826 }
827}
828
829impl fmt::Debug for Diagnostic {
830 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
831 write!(f, "{}@{}", self.error_code().raw(), self.origin.wire_code())
832 }
833}
834
835fn fmt_compact_code(f: &mut fmt::Formatter<'_>, raw: u16) -> fmt::Result {
836 write!(f, "{raw}")
837}
838
839#[cfg(test)]
840mod tests {
841 use super::{
842 Diagnostic, DiagnosticCode, DiagnosticDetail, ErrorClass, ErrorCode, ErrorOrigin,
843 QueryProjectionCode, QueryReadAdmissionCode, SqlFeatureCode, SqlLoweringCode,
844 SqlWriteBoundaryCode,
845 registry::{DETAIL_ERROR_CODES, ORDERED_ERROR_CODES},
846 };
847
848 #[test]
849 fn diagnostic_from_code_uses_default_origin() {
850 let diagnostic = Diagnostic::from_code(DiagnosticCode::QueryPlan);
851
852 assert_eq!(diagnostic.code(), DiagnosticCode::QueryPlan);
853 assert_eq!(diagnostic.origin(), ErrorOrigin::Query);
854 }
855
856 #[test]
857 fn diagnostic_code_reports_broad_class() {
858 assert_eq!(
859 DiagnosticCode::QueryUnsupportedSqlFeature.class(),
860 ErrorClass::Unsupported
861 );
862 assert_eq!(
863 DiagnosticCode::QuerySqlSurfaceMismatch.class(),
864 ErrorClass::Unsupported
865 );
866 assert_eq!(DiagnosticCode::QueryPlan.class(), ErrorClass::Query);
867 assert_eq!(
868 DiagnosticCode::StoreCorruption.class(),
869 ErrorClass::Corruption
870 );
871 }
872
873 #[test]
874 fn class_and_origin_wire_codes_round_trip() {
875 for (class, raw) in [
876 (ErrorClass::Query, 1),
877 (ErrorClass::Corruption, 2),
878 (ErrorClass::IncompatiblePersistedFormat, 3),
879 (ErrorClass::NotFound, 4),
880 (ErrorClass::Internal, 5),
881 (ErrorClass::Conflict, 6),
882 (ErrorClass::Unsupported, 7),
883 (ErrorClass::InvariantViolation, 8),
884 ] {
885 assert_eq!(class.wire_code(), raw);
886 assert_eq!(ErrorClass::from_wire_code(raw), Some(class));
887 assert_eq!(format!("{class:?}"), raw.to_string());
888 }
889
890 for (origin, raw) in [
891 (ErrorOrigin::Cursor, 1),
892 (ErrorOrigin::Executor, 2),
893 (ErrorOrigin::Identity, 3),
894 (ErrorOrigin::Index, 4),
895 (ErrorOrigin::Interface, 5),
896 (ErrorOrigin::Planner, 6),
897 (ErrorOrigin::Query, 7),
898 (ErrorOrigin::Recovery, 8),
899 (ErrorOrigin::Response, 9),
900 (ErrorOrigin::Runtime, 10),
901 (ErrorOrigin::Serialize, 11),
902 (ErrorOrigin::Store, 12),
903 ] {
904 assert_eq!(origin.wire_code(), raw);
905 assert_eq!(ErrorOrigin::from_known_wire_code(raw), Some(origin));
906 assert_eq!(ErrorOrigin::from_wire_code(raw), origin);
907 assert_eq!(format!("{origin:?}"), raw.to_string());
908 }
909
910 assert_eq!(ErrorClass::from_wire_code(0), None);
911 assert_eq!(ErrorOrigin::from_known_wire_code(0), None);
912 assert_eq!(ErrorOrigin::from_wire_code(0), ErrorOrigin::Runtime);
913 }
914
915 #[test]
916 fn public_error_codes_are_sequential() {
917 let first = ORDERED_ERROR_CODES
918 .first()
919 .expect("public error-code registry is non-empty")
920 .raw();
921
922 assert_eq!(first, 1);
923
924 for (index, code) in ORDERED_ERROR_CODES.iter().enumerate() {
925 let expected = first + u16::try_from(index).expect("test error-code index fits u16");
926 assert_eq!(code.raw(), expected);
927 assert_eq!(ErrorCode::known(code.raw()), Some(*code));
928 assert!(code.is_known());
929 }
930
931 let last = ORDERED_ERROR_CODES
932 .last()
933 .expect("public error-code registry is non-empty")
934 .raw();
935
936 assert_eq!(last, 199);
937 }
938
939 #[test]
940 fn all_public_error_codes_round_trip_through_diagnostic_parts() {
941 let first = ORDERED_ERROR_CODES
942 .first()
943 .expect("public error-code registry is non-empty")
944 .raw();
945 let last = ORDERED_ERROR_CODES
946 .last()
947 .expect("public error-code registry is non-empty")
948 .raw();
949
950 for raw in first..=last {
951 let code = ErrorCode::from_raw(raw);
952 let diagnostic_code = code.diagnostic_code();
953 let diagnostic_detail = code.diagnostic_detail();
954 let rebuilt = ErrorCode::from_parts(diagnostic_code, diagnostic_detail);
955
956 assert_eq!(rebuilt.raw(), raw);
957
958 let diagnostic = code.diagnostic(ErrorOrigin::Runtime);
959
960 assert_eq!(diagnostic.code(), diagnostic_code);
961 assert_eq!(diagnostic.detail(), diagnostic_detail.as_ref());
962 assert_eq!(diagnostic.error_code().raw(), raw);
963 }
964 }
965
966 #[test]
967 fn invalid_raw_error_codes_fail_closed_to_runtime_internal() {
968 for raw in [0, 203, u16::MAX] {
969 let code = ErrorCode::from_raw(raw);
970
971 assert_eq!(ErrorCode::known(raw), None);
972 assert!(!code.is_known());
973 assert_eq!(code.diagnostic_code(), DiagnosticCode::RuntimeInternal);
974 assert_eq!(code.diagnostic_detail(), None);
975 assert_eq!(code.class(), ErrorClass::Internal);
976
977 let diagnostic = code.diagnostic(ErrorOrigin::Query);
978
979 assert_eq!(diagnostic.code(), DiagnosticCode::RuntimeInternal);
980 assert_eq!(diagnostic.origin(), ErrorOrigin::Query);
981 assert_eq!(diagnostic.detail(), None);
982 assert_eq!(diagnostic.error_code(), ErrorCode::RUNTIME_INTERNAL);
983 }
984 }
985
986 #[test]
987 fn from_parts_requires_detail_to_match_broad_code() {
988 let detail = Some(DiagnosticDetail::UnsupportedSqlFeature {
989 feature: SqlFeatureCode::Join,
990 });
991
992 assert_eq!(
993 ErrorCode::from_parts(DiagnosticCode::QueryUnsupportedSqlFeature, detail),
994 ErrorCode::SQL_FEATURE_JOIN
995 );
996 assert_eq!(
997 ErrorCode::from_parts(DiagnosticCode::QueryPlan, detail),
998 ErrorCode::QUERY_PLAN
999 );
1000 }
1001
1002 #[test]
1003 fn detail_bearing_registry_entries_round_trip_directly() {
1004 assert!(!DETAIL_ERROR_CODES.is_empty());
1005
1006 for &(code, diagnostic_code, detail) in DETAIL_ERROR_CODES {
1007 assert_eq!(ErrorCode::from_parts(diagnostic_code, Some(detail)), code);
1008 assert_eq!(code.diagnostic_code(), diagnostic_code);
1009 assert_eq!(code.diagnostic_detail(), Some(detail));
1010 assert_eq!(detail.diagnostic_code(), diagnostic_code);
1011 }
1012 }
1013
1014 #[test]
1015 fn diagnostic_detail_reports_generated_broad_code() {
1016 let detail = DiagnosticDetail::UnsupportedSqlFeature {
1017 feature: SqlFeatureCode::Join,
1018 };
1019
1020 assert_eq!(
1021 detail.diagnostic_code(),
1022 DiagnosticCode::QueryUnsupportedSqlFeature
1023 );
1024 assert_eq!(format!("{detail:?}"), "65");
1025 }
1026
1027 #[test]
1028 fn public_error_codes_reconstruct_shifted_details() {
1029 assert_eq!(
1030 ErrorCode::QUERY_UNKNOWN_AGGREGATE_TARGET_FIELD.diagnostic_code(),
1031 DiagnosticCode::QueryUnknownAggregateTargetField
1032 );
1033 assert_eq!(
1034 ErrorCode::SQL_FEATURE_JOIN.diagnostic_detail(),
1035 Some(DiagnosticDetail::UnsupportedSqlFeature {
1036 feature: SqlFeatureCode::Join,
1037 })
1038 );
1039 assert_eq!(
1040 ErrorCode::QUERY_PROJECTION_NUMERIC_LITERAL_REQUIRED.diagnostic_detail(),
1041 Some(DiagnosticDetail::QueryProjection {
1042 reason: QueryProjectionCode::NumericLiteralRequired,
1043 })
1044 );
1045 assert_eq!(
1046 ErrorCode::QUERY_READ_PUBLIC_REQUIRES_LIMIT.diagnostic_detail(),
1047 Some(DiagnosticDetail::QueryReadAdmission {
1048 reason: QueryReadAdmissionCode::PublicQueryRequiresLimit,
1049 })
1050 );
1051 assert_eq!(
1052 ErrorCode::SQL_LOWERING_DISTINCT_ORDER_BY_PROJECTION.diagnostic_detail(),
1053 Some(DiagnosticDetail::SqlLowering {
1054 reason: SqlLoweringCode::DistinctOrderByProjection,
1055 })
1056 );
1057 assert_eq!(
1058 ErrorCode::SQL_WRITE_RETURNING_RESPONSE_TOO_LARGE.diagnostic_detail(),
1059 Some(DiagnosticDetail::SqlWriteBoundary {
1060 boundary: SqlWriteBoundaryCode::ReturningResponseTooLarge,
1061 })
1062 );
1063 assert_eq!(
1064 ErrorCode::SQL_WRITE_RETURNING_ROWS_TOO_MANY.diagnostic_detail(),
1065 Some(DiagnosticDetail::SqlWriteBoundary {
1066 boundary: SqlWriteBoundaryCode::ReturningRowsTooMany,
1067 })
1068 );
1069 }
1070}